diff --git a/Taskfile.yml b/Taskfile.yml new file mode 100644 index 0000000..fd9c0c5 --- /dev/null +++ b/Taskfile.yml @@ -0,0 +1,99 @@ +# yaml-language-server: $schema=https://taskfile.dev/schema.json + +version: '3' + +tasks: + default: + desc: List available tasks + cmds: + - task --list + silent: true + + test: + desc: Run generation, tests, datastore integration tests, vet, and builds + cmds: + - task: test:generate + - task: test:unit + - task: test:race + - task: test:datastores + - task: test:vet + - task: test:build + - git diff --check + + test:generate: + internal: true + cmd: go generate ./... + + test:unit: + internal: true + cmd: go test ./... + + test:race: + internal: true + cmd: go test -race ./west/... ./westport/... + + test:datastores: + internal: true + preconditions: + - sh: docker version >/dev/null + msg: Docker is required for PostgreSQL and MySQL integration tests + cmd: | + set -eu + + postgres_container="west-test-postgres-$$" + mysql_container="west-test-mysql-$$" + cleanup() { + docker rm -f "$postgres_container" "$mysql_container" >/dev/null 2>&1 || true + } + trap cleanup EXIT + + docker run -d --rm \ + --name "$postgres_container" \ + -e POSTGRES_USER=west \ + -e POSTGRES_PASSWORD=westpass \ + -e POSTGRES_DB=west \ + -p 127.0.0.1::5432 \ + postgres:18-alpine >/dev/null + + docker run -d --rm \ + --name "$mysql_container" \ + -e MYSQL_USER=west \ + -e MYSQL_PASSWORD=westpass \ + -e MYSQL_DATABASE=west \ + -e MYSQL_ROOT_PASSWORD=rootpass \ + -p 127.0.0.1::3306 \ + mysql:8.4 >/dev/null + + attempts=0 + until docker exec "$postgres_container" pg_isready -U west -d west >/dev/null 2>&1; do + attempts=$((attempts + 1)) + if [ "$attempts" -ge 60 ]; then + echo "PostgreSQL did not become ready" >&2 + exit 1 + fi + sleep 1 + done + + attempts=0 + until docker exec "$mysql_container" mysqladmin ping -h127.0.0.1 -uroot -prootpass >/dev/null 2>&1; do + attempts=$((attempts + 1)) + if [ "$attempts" -ge 60 ]; then + echo "MySQL did not become ready" >&2 + exit 1 + fi + sleep 1 + done + + postgres_address=$(docker port "$postgres_container" 5432) + mysql_address=$(docker port "$mysql_container" 3306) + WEST_TEST_POSTGRES_DSN="postgres://west:westpass@${postgres_address}/west?sslmode=disable" \ + WEST_TEST_MYSQL_DSN="mysql://west:westpass@${mysql_address}/west" \ + go test ./westport/db -run '^TestDatastoreSchema$' -count=1 + + test:vet: + internal: true + cmd: go vet ./... + + test:build: + internal: true + cmd: go build ./cmd/west ./cmd/snap diff --git a/cmd/snap/snap.go b/cmd/snap/snap.go index 456ed7e..c14c5ac 100644 --- a/cmd/snap/snap.go +++ b/cmd/snap/snap.go @@ -6,6 +6,7 @@ import ( "github.com/sprisa/west/west" "github.com/sprisa/west/westport/db" + "github.com/sprisa/west/westport/localconfig" "github.com/sprisa/x/env" "github.com/sprisa/x/errutil" l "github.com/sprisa/x/log" @@ -22,6 +23,7 @@ func init() { // The db should be located under the user common dir, not root // Use snap common dir for configs db.DBFilePath = filepath.Join(configDirPath, db.DBFilePath) + localconfig.FilePath = filepath.Join(configDirPath, localconfig.FilePath) } func main() { diff --git a/go.mod b/go.mod index 875cced..c914db0 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/sprisa/west -go 1.25.6 +go 1.27.0 tool ( github.com/99designs/gqlgen @@ -16,9 +16,11 @@ require ( github.com/anandvarma/namegen v1.1.1 github.com/cqroot/prompt v0.9.4 github.com/go-acme/lego/v4 v4.26.0 + github.com/go-sql-driver/mysql v1.10.0 github.com/goccy/go-yaml v1.19.2 github.com/golang-jwt/jwt/v5 v5.3.0 github.com/hashicorp/go-multierror v1.1.1 + github.com/jackc/pgx/v5 v5.10.0 github.com/miekg/dns v1.1.68 github.com/rs/zerolog v1.34.0 github.com/samber/lo v1.52.0 @@ -39,6 +41,7 @@ require ( require ( ariga.io/atlas v0.32.1-0.20250325101103-175b25e1c1b9 // indirect + filippo.io/edwards25519 v1.2.0 // indirect github.com/agext/levenshtein v1.2.3 // indirect github.com/agnivade/levenshtein v1.2.1 // indirect github.com/alexflint/go-arg v1.5.1 // indirect @@ -76,6 +79,9 @@ require ( github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/hcl/v2 v2.18.1 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect diff --git a/go.sum b/go.sum index ea18fc6..1071c2d 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,8 @@ entgo.io/contrib v0.7.0 h1:4Ghx8O0rqSMmca3FIJ6QyZbQAoLvdzWqLMl1MbHFEEw= entgo.io/contrib v0.7.0/go.mod h1:zbPSUrbn+6dfyv8S9HWEvn1MyGpO95ik2lUNgaqWTt4= entgo.io/ent v0.14.5 h1:Rj2WOYJtCkWyFo6a+5wB3EfBRP0rnx1fMk6gGA0UUe4= entgo.io/ent v0.14.5/go.mod h1:zTzLmWtPvGpmSwtkaayM2cm5m819NdM7z7tYPq3vN0U= +filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= +filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= github.com/99designs/gqlgen v0.17.81 h1:kCkN/xVyRb5rEQpuwOHRTYq83i0IuTQg9vdIiwEerTs= github.com/99designs/gqlgen v0.17.81/go.mod h1:vgNcZlLwemsUhYim4dC1pvFP5FX0pr2Y+uYUoHFb1ig= github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= @@ -102,6 +104,8 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4= github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4= +github.com/go-sql-driver/mysql v1.10.0 h1:Q+1LV8DkHJvSYAdR83XzuhDaTykuDx0l6fkXxoWCWfw= +github.com/go-sql-driver/mysql v1.10.0/go.mod h1:M+cqaI7+xxXGG9swrdeUIoPG3Y3KCkF0pZej+SK+nWk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= @@ -149,6 +153,14 @@ github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl/v2 v2.18.1 h1:6nxnOJFku1EuSawSD81fuviYUV8DxFr3fp2dUi3ZYSo= github.com/hashicorp/hcl/v2 v2.18.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.10.0 h1:VhSvgU2jSli8o3AqIEOTJr7rZwAEUVo4E4XhR94Zfr0= +github.com/jackc/pgx/v5 v5.10.0/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= diff --git a/util/auth/auth.go b/util/auth/auth.go index f31eb1a..fdca9eb 100644 --- a/util/auth/auth.go +++ b/util/auth/auth.go @@ -3,9 +3,9 @@ package auth import "github.com/golang-jwt/jwt/v5" type TokenClaims struct { - Endpoint string `json:"endpoint"` - IP string `json:"ip"` - Ca string `json:"ca"` - PortIP string `json:"port_ip"` + Endpoint string `json:"endpoint"` + EndpointAddresses []string `json:"endpoint_addresses"` + IP string `json:"ip"` + Ca string `json:"ca"` jwt.RegisteredClaims } diff --git a/util/ipconv/ipconv_test.go b/util/ipconv/ipconv_test.go index f12094c..53d45b9 100644 --- a/util/ipconv/ipconv_test.go +++ b/util/ipconv/ipconv_test.go @@ -18,7 +18,7 @@ func TestIPv4ToInt(t *testing.T) { } { got, err := IPv4ToInt(c.in) if got != c.want || err != nil { - t.Errorf("IPv4ToInt(%q) == %q, want %q", c.in, got, c.want) + t.Errorf("IPv4ToInt(%q) == %v, want %v", c.in, got, c.want) } } } @@ -32,7 +32,7 @@ func TestIPv4ToIntError(t *testing.T) { } { got, err := IPv4ToInt(c.in) if err == nil { - t.Errorf("IPv4ToInt(%q) == %q, want %q", c.in, got, c.want) + t.Errorf("IPv4ToInt(%q) == %v, want %v", c.in, got, c.want) } } } @@ -48,7 +48,7 @@ func TestIPv6ToInt(t *testing.T) { } { got, err := IPv6ToInt(c.in) if got != c.want || err != nil { - t.Errorf("IPv6ToInt(%q) == %q, want %q", c.in.To16(), got, c.want) + t.Errorf("IPv6ToInt(%q) == %v, want %v", c.in.To16(), got, c.want) } } } @@ -82,7 +82,7 @@ func TestIntToIPv4(t *testing.T) { } { got := IntToIPv4(c.in) if !got.Equal(c.want) { - t.Errorf("IntToIPv4(%q) == %q, want %q", c.in, got, c.want) + t.Errorf("IntToIPv4(%v) == %q, want %q", c.in, got, c.want) } } } @@ -101,7 +101,7 @@ func TestIntToIPv6(t *testing.T) { } { got := IntToIPv6(c.in[0], c.in[1]) if !got.Equal(c.want) { - t.Errorf("IntToIPv6(%q) == %q, want %q", c.in, got, c.want) + t.Errorf("IntToIPv6(%v) == %q, want %q", c.in, got, c.want) } } } diff --git a/west/gql/generated.go b/west/gql/generated.go index df680c3..1fbcc86 100644 --- a/west/gql/generated.go +++ b/west/gql/generated.go @@ -17,11 +17,12 @@ func (v *ProvisionDeviceInput) GetToken() string { return v.Token } // ProvisionDeviceProvision_deviceProvisionDeviceResponse includes the requested fields of the GraphQL type ProvisionDeviceResponse. type ProvisionDeviceProvision_deviceProvisionDeviceResponse struct { - Name string `json:"name"` - Ca string `json:"ca"` - Cert string `json:"cert"` - Key string `json:"key"` - NetworkCipher string `json:"networkCipher"` + Name string `json:"name"` + Ca string `json:"ca"` + Cert string `json:"cert"` + Key string `json:"key"` + NetworkCipher string `json:"networkCipher"` + Lighthouses []ProvisionDeviceProvision_deviceProvisionDeviceResponseLighthousesLighthouseConfig `json:"lighthouses"` } // GetName returns ProvisionDeviceProvision_deviceProvisionDeviceResponse.Name, and is useful for accessing the field via an interface. @@ -41,6 +42,27 @@ func (v *ProvisionDeviceProvision_deviceProvisionDeviceResponse) GetNetworkCiphe return v.NetworkCipher } +// GetLighthouses returns ProvisionDeviceProvision_deviceProvisionDeviceResponse.Lighthouses, and is useful for accessing the field via an interface. +func (v *ProvisionDeviceProvision_deviceProvisionDeviceResponse) GetLighthouses() []ProvisionDeviceProvision_deviceProvisionDeviceResponseLighthousesLighthouseConfig { + return v.Lighthouses +} + +// ProvisionDeviceProvision_deviceProvisionDeviceResponseLighthousesLighthouseConfig includes the requested fields of the GraphQL type LighthouseConfig. +type ProvisionDeviceProvision_deviceProvisionDeviceResponseLighthousesLighthouseConfig struct { + OverlayIp string `json:"overlayIp"` + Endpoint string `json:"endpoint"` +} + +// GetOverlayIp returns ProvisionDeviceProvision_deviceProvisionDeviceResponseLighthousesLighthouseConfig.OverlayIp, and is useful for accessing the field via an interface. +func (v *ProvisionDeviceProvision_deviceProvisionDeviceResponseLighthousesLighthouseConfig) GetOverlayIp() string { + return v.OverlayIp +} + +// GetEndpoint returns ProvisionDeviceProvision_deviceProvisionDeviceResponseLighthousesLighthouseConfig.Endpoint, and is useful for accessing the field via an interface. +func (v *ProvisionDeviceProvision_deviceProvisionDeviceResponseLighthousesLighthouseConfig) GetEndpoint() string { + return v.Endpoint +} + // ProvisionDeviceResponse is returned by ProvisionDevice on success. type ProvisionDeviceResponse struct { Provision_device ProvisionDeviceProvision_deviceProvisionDeviceResponse `json:"provision_device"` @@ -68,6 +90,10 @@ mutation ProvisionDevice ($input: ProvisionDeviceInput!) { cert key networkCipher + lighthouses { + overlayIp + endpoint + } } } ` diff --git a/west/gql/west.graphql b/west/gql/west.graphql index c2b5e43..e78f44c 100644 --- a/west/gql/west.graphql +++ b/west/gql/west.graphql @@ -5,5 +5,9 @@ mutation ProvisionDevice($input: ProvisionDeviceInput!) { cert key networkCipher + lighthouses { + overlayIp + endpoint + } } } diff --git a/west/http.go b/west/http.go new file mode 100644 index 0000000..89df0d6 --- /dev/null +++ b/west/http.go @@ -0,0 +1,55 @@ +package west + +import ( + "context" + "crypto/tls" + "errors" + "net" + "net/http" + "net/url" + "time" + + "github.com/Khan/genqlient/graphql" + "github.com/sprisa/west/west/gql" +) + +func provisionWithFailover(ctx context.Context, endpoint *url.URL, addresses []string, input gql.ProvisionDeviceInput) (*gql.ProvisionDeviceResponse, error) { + perAttempt := 10 * time.Second + var errs error + + for _, addr := range addresses { + attemptCtx, cancel := context.WithTimeout(ctx, perAttempt) + httpClient := httpClientForAddress(endpoint, addr) + client := graphql.NewClient(endpoint.String(), httpClient) + data, err := gql.ProvisionDevice(attemptCtx, client, input) + cancel() + if err == nil { + return data, nil + } + errs = errors.Join(errs, err) + } + + attemptCtx, cancel := context.WithTimeout(ctx, perAttempt) + defer cancel() + client := graphql.NewClient(endpoint.String(), &http.Client{Timeout: perAttempt}) + data, err := gql.ProvisionDevice(attemptCtx, client, input) + if err == nil { + return data, nil + } + return nil, errors.Join(errs, err) +} + +func httpClientForAddress(endpoint *url.URL, address string) *http.Client { + dialer := &net.Dialer{Timeout: 5 * time.Second} + transport := http.DefaultTransport.(*http.Transport).Clone() + transport.DialContext = func(ctx context.Context, network, _ string) (net.Conn, error) { + return dialer.DialContext(ctx, network, address) + } + if endpoint.Scheme == "https" { + if transport.TLSClientConfig == nil { + transport.TLSClientConfig = &tls.Config{} + } + transport.TLSClientConfig.ServerName = endpoint.Hostname() + } + return &http.Client{Transport: transport, Timeout: 10 * time.Second} +} diff --git a/west/http_test.go b/west/http_test.go new file mode 100644 index 0000000..901e46c --- /dev/null +++ b/west/http_test.go @@ -0,0 +1,69 @@ +package west + +import ( + "fmt" + "io" + "net/http" + "net/http/httptest" + "net/url" + "testing" +) + +func TestHTTPClientForAddressPreservesHost(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprint(w, r.Host) + })) + defer server.Close() + serverURL, err := url.Parse(server.URL) + if err != nil { + t.Fatal(err) + } + endpoint, err := url.Parse("http://west.invalid/api") + if err != nil { + t.Fatal(err) + } + client := httpClientForAddress(endpoint, serverURL.Host) + response, err := client.Get(endpoint.String()) + if err != nil { + t.Fatal(err) + } + defer response.Body.Close() + body, err := io.ReadAll(response.Body) + if err != nil { + t.Fatal(err) + } + if got := string(body); got != endpoint.Host { + t.Fatalf("request host = %q, want %q", got, endpoint.Host) + } +} + +func TestHTTPClientForAddressSkipsRefusedConnection(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + defer server.Close() + serverURL, err := url.Parse(server.URL) + if err != nil { + t.Fatal(err) + } + endpoint, err := url.Parse("http://west.invalid/api") + if err != nil { + t.Fatal(err) + } + + badClient := httpClientForAddress(endpoint, "127.0.0.1:1") + _, err = badClient.Get(endpoint.String()) + if err == nil { + t.Fatal("expected refused connection to fail") + } + + goodClient := httpClientForAddress(endpoint, serverURL.Host) + resp, err := goodClient.Get(endpoint.String()) + if err != nil { + t.Fatal(err) + } + resp.Body.Close() + if resp.StatusCode != http.StatusOK { + t.Fatalf("status = %d, want 200", resp.StatusCode) + } +} diff --git a/west/start.go b/west/start.go index f9d5eed..da1e026 100644 --- a/west/start.go +++ b/west/start.go @@ -6,13 +6,10 @@ import ( "errors" "fmt" "io" - "net" - "net/http" "net/url" "os" "time" - "github.com/Khan/genqlient/graphql" "github.com/golang-jwt/jwt/v5" "github.com/sprisa/west" "github.com/sprisa/west/config" @@ -84,8 +81,7 @@ var StartCommand = &cli.Command{ l.Log.Debug().Msgf("claims: %+v", claims) - client := graphql.NewClient(endpoint, http.DefaultClient) - data, err := gql.ProvisionDevice(ctx, client, gql.ProvisionDeviceInput{ + data, err := provisionWithFailover(ctx, url, claims.EndpointAddresses, gql.ProvisionDeviceInput{ Token: token, }) if err != nil { @@ -104,21 +100,25 @@ var StartCommand = &cli.Command{ } } + staticHostMap := config.StaticHostMap{} + lighthouseHosts := make([]string, 0, len(dvc.Lighthouses)) + for _, lighthouse := range dvc.Lighthouses { + staticHostMap[lighthouse.OverlayIp] = []string{lighthouse.Endpoint} + lighthouseHosts = append(lighthouseHosts, lighthouse.OverlayIp) + } + if len(lighthouseHosts) == 0 { + return errors.New("provisioning returned no lighthouses") + } + cfg := &config.Config{ Pki: config.Pki{ Ca: dvc.Ca, Cert: dvc.Cert, Key: dvc.Key, }, - StaticHostMap: config.StaticHostMap{ - claims.PortIP: []string{ - net.JoinHostPort(url.Hostname(), "4242"), - }, - }, + StaticHostMap: staticHostMap, Lighthouse: config.Lighthouse{ - Hosts: []string{ - claims.PortIP, - }, + Hosts: lighthouseHosts, }, Tun: config.Tun{ Disabled: disableTun, diff --git a/westport/acme/certs.go b/westport/acme/certs.go index a94f234..188dabd 100644 --- a/westport/acme/certs.go +++ b/westport/acme/certs.go @@ -20,6 +20,7 @@ func GetCertificate( httpProvider *HTTPProvider, dnsProvider *DNSProvider, ) (*tls.Certificate, error) { + var cachedCert *tls.Certificate // Check for existing key if settings.TLSCert != nil && len(*settings.TLSCert) > 0 && settings.TLSCertKey != nil && len(*settings.TLSCertKey) > 0 { @@ -29,19 +30,25 @@ func GetCertificate( if err != nil { return nil, errutil.WrapErr(err, "error parsing cert") } - // Parse certificate to check expiry x509Cert, err := x509.ParseCertificate(cert.Certificate[0]) if err != nil { return nil, errutil.WrapErr(err, "error parsing x509 cert") } + now := time.Now() + if now.After(x509Cert.NotBefore) && now.Before(x509Cert.NotAfter) { + cachedCert = &cert + } + // Check if certificate needs renewal (30 days before expiry) - if x509Cert.NotAfter.Unix()-time.Now().Unix() < 30*24*3600 { + if cachedCert == nil { + l.Log.Warn().Msg("Cached certificate is not currently valid. Renewing now.") + } else if x509Cert.NotAfter.Unix()-now.Unix() < 30*24*3600 { l.Log.Warn().Msg("Certificate expiring in < 30 days. Renewing now.") } else { l.Log.Info().Msg("Using cached TLS certificate") - return &cert, nil + return cachedCert, nil } } @@ -85,6 +92,10 @@ func GetCertificate( certs, err := client.Certificate.Obtain(request) if err != nil { + if cachedCert != nil { + l.Log.Warn().Err(err).Msg("Certificate renewal failed; continuing with cached certificate") + return cachedCert, nil + } return nil, errutil.WrapErr(err, "failed to obtain certificate") } diff --git a/westport/add.go b/westport/add.go index e9c97bd..16bf343 100644 --- a/westport/add.go +++ b/westport/add.go @@ -10,12 +10,13 @@ import ( "github.com/golang-jwt/jwt/v5" "github.com/sprisa/west/util/auth" - "github.com/sprisa/west/util/info" "github.com/sprisa/west/util/ipconv" "github.com/sprisa/west/westport/db" "github.com/sprisa/west/westport/db/ent" + "github.com/sprisa/west/westport/db/ent/lighthouse" "github.com/sprisa/west/westport/db/helpers" "github.com/sprisa/west/westport/db/migrate" + "github.com/sprisa/west/westport/localconfig" "github.com/sprisa/x/errutil" "github.com/urfave/cli/v3" ) @@ -48,7 +49,15 @@ var AddCommand = &cli.Command{ return errutil.WrapErr(err, "error parsing ip `%s`", ipStr) } - client, err := db.OpenDB() + err = readEncryptionPassword() + if err != nil { + return err + } + localCfg, err := localconfig.Load() + if err != nil { + return errutil.WrapErr(err, "load local west-port config") + } + client, err := db.OpenDB(ctx, localCfg.Datastore) if err != nil { return errutil.WrapErr(err, "error opening db") } @@ -58,11 +67,6 @@ var AddCommand = &cli.Command{ return errutil.WrapErr(err, "error migrating db") } - err = readEncryptionPassword() - if err != nil { - return err - } - settings, err := client.Settings.Query().Only(ctx) if err != nil { if ent.IsNotFound(err) { @@ -77,30 +81,42 @@ var AddCommand = &cli.Command{ nebulaIp := netip.PrefixFrom(ip, settings.Cidr.Bits()) + ports, err := client.Lighthouse.Query().Order(ent.Asc(lighthouse.FieldIP)).All(ctx) + if err != nil { + return errutil.WrapErr(err, "error loading west ports") + } + if len(ports) == 0 { + return errors.New("no west ports are installed") + } + var endpoint url.URL - if settings.DomainZone != "" { + if settings.DomainZone != "" && len(settings.LetsencryptRegistration) > 0 { endpoint = url.URL{ Scheme: "https", Host: settings.DomainZone, Path: "api", } } else { - publicIp, err := info.GetPublicIP() - if err != nil { - return errutil.WrapErr(err, "error getting public ip") + host := settings.DomainZone + if host == "" { + host = ports[0].APIEndpoint } endpoint = url.URL{ Scheme: "http", - Host: publicIp.String(), + Host: host, Path: "api", } } + endpointAddresses := make([]string, 0, len(ports)) + for _, westPort := range ports { + endpointAddresses = append(endpointAddresses, westPort.APIEndpoint) + } claims := &auth.TokenClaims{ - Endpoint: endpoint.String(), - IP: nebulaIp.String(), - Ca: string(settings.CaCrt), - PortIP: settings.PortOverlayIP.ToIPV4().String(), + Endpoint: endpoint.String(), + EndpointAddresses: endpointAddresses, + IP: nebulaIp.String(), + Ca: string(settings.CaCrt), RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate( // 1 year @@ -119,15 +135,29 @@ var AddCommand = &cli.Command{ if err != nil { return errutil.WrapErr(err, "error converting ip") } - - _, err = client.Device.Create(). + tx, err := client.Tx(ctx) + if err != nil { + return err + } + defer tx.Rollback() + host, err := tx.Host.Create().SetIP(ipInt).Save(ctx) + if err != nil { + if ent.IsConstraintError(err) { + return fmt.Errorf("overlay IP %s is already in use", ip) + } + return errutil.WrapErr(err, "reserve overlay IP") + } + if _, err := tx.Device.Create(). SetName(name). SetIP(ipInt). SetToken(helpers.EncryptedBytes(token)). - Save(ctx) - if err != nil { + SetHostID(host.ID). + Save(ctx); err != nil { return errutil.WrapErr(err, "error saving device") } + if err := tx.Commit(); err != nil { + return err + } println(token) return nil diff --git a/westport/db/client.go b/westport/db/client.go index e217953..275eb5f 100644 --- a/westport/db/client.go +++ b/westport/db/client.go @@ -1,12 +1,20 @@ package db import ( + "context" "database/sql" "database/sql/driver" + "errors" "fmt" - "os" + "net/url" + "strings" + "sync" + "time" "entgo.io/ent/dialect" + entsql "entgo.io/ent/dialect/sql" + "github.com/go-sql-driver/mysql" + _ "github.com/jackc/pgx/v5/stdlib" "github.com/sprisa/west/westport/db/ent" _ "github.com/sprisa/west/westport/db/ent/runtime" "github.com/sprisa/x/errutil" @@ -14,27 +22,128 @@ import ( "modernc.org/sqlite" ) -var DBFilePath string = "westdb" +const ( + DatastoreSQLite = "sqlite" + DatastorePostgres = "postgres" + DatastoreMySQL = "mysql" +) + +var DBFilePath = "westdb" -func OpenDB() (*ent.Client, error) { - sql.Register("sqlite3", &sqliteDriver{}) - l.Log.Debug().Msgf("DB Open: %s", DBFilePath) - _, err := os.Stat(DBFilePath) - if err != nil && os.IsNotExist(err) == false { +var registerSQLite sync.Once + +func DatastoreType(dataSource string) (string, error) { + switch { + case dataSource == DatastoreSQLite, strings.HasPrefix(dataSource, "sqlite://"): + return DatastoreSQLite, nil + case strings.HasPrefix(dataSource, "postgres://"), strings.HasPrefix(dataSource, "postgresql://"): + return DatastorePostgres, nil + case strings.HasPrefix(dataSource, "mysql://"): + return DatastoreMySQL, nil + default: + return "", fmt.Errorf("unsupported datastore %q; use sqlite, sqlite://, postgres://, postgresql://, or mysql://", dataSource) + } +} + +func OpenDB(ctx context.Context, dataSource string) (*ent.Client, error) { + typeName, err := DatastoreType(dataSource) + if err != nil { + return nil, err + } + + driverName, entDialect, dsn := "", "", "" + switch typeName { + case DatastoreSQLite: + registerSQLite.Do(func() { + sql.Register("west-sqlite", &sqliteDriver{}) + }) + driverName, entDialect = "west-sqlite", dialect.SQLite + path := DBFilePath + if dataSource != DatastoreSQLite { + path = strings.TrimPrefix(dataSource, "sqlite://") + if path == "" { + return nil, errors.New("sqlite datastore path cannot be empty") + } + } + dsn = fmt.Sprintf("file:%s?mode=rwc&cache=shared&_fk=1", path) + case DatastorePostgres: + u, err := url.Parse(dataSource) + if err != nil || strings.TrimPrefix(u.Path, "/") == "" { + return nil, errors.New("postgres datastore must include a database name") + } + driverName, entDialect, dsn = "pgx", dialect.Postgres, dataSource + case DatastoreMySQL: + driverName, entDialect = "mysql", dialect.MySQL + dsn, err = mysqlDSN(dataSource) + if err != nil { + return nil, err + } + } + + l.Log.Debug().Str("datastore", typeName).Msg("Opening database") + sqlDB, err := sql.Open(driverName, dsn) + if err != nil { return nil, err } + sqlDB.SetMaxOpenConns(20) + sqlDB.SetMaxIdleConns(10) + sqlDB.SetConnMaxIdleTime(5 * time.Minute) + sqlDB.SetConnMaxLifetime(30 * time.Minute) - return ent.Open( - dialect.SQLite, - fmt.Sprintf("file:%s?mode=rwc&cache=shared&_fk=1", DBFilePath), - ) + pingCtx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + if err := sqlDB.PingContext(pingCtx); err != nil { + sqlDB.Close() + return nil, fmt.Errorf("connect to %s datastore: %w", typeName, err) + } + + drv := entsql.OpenDB(entDialect, sqlDB) + return ent.NewClient(ent.Driver(drv)), nil +} + +func mysqlDSN(dataSource string) (string, error) { + u, err := url.Parse(dataSource) + if err != nil { + return "", fmt.Errorf("parse mysql datastore: %w", err) + } + database := strings.TrimPrefix(u.Path, "/") + if database == "" { + return "", errors.New("mysql datastore must include a database name") + } + username, password := "", "" + if u.User != nil { + username = u.User.Username() + password, _ = u.User.Password() + } + config := &mysql.Config{ + User: username, + Passwd: password, + Net: "tcp", + Addr: u.Host, + DBName: database, + ParseTime: true, + Loc: time.UTC, + } + dsn := config.FormatDSN() + if u.RawQuery != "" { + separator := "?" + if strings.Contains(dsn, "?") { + separator = "&" + } + dsn += separator + u.RawQuery + } + config, err = mysql.ParseDSN(dsn) + if err != nil { + return "", err + } + config.ParseTime = true + return config.FormatDSN(), nil } type sqliteDriver struct { sqlite.Driver } -// https://github.com/ent/ent/discussions/1667#discussioncomment-1132296 func (d sqliteDriver) Open(name string) (driver.Conn, error) { conn, err := d.Driver.Open(name) if err != nil { @@ -45,7 +154,7 @@ func (d sqliteDriver) Open(name string) (driver.Conn, error) { }) if _, err := c.Exec("PRAGMA foreign_keys = on;", nil); err != nil { conn.Close() - return nil, errutil.WrapErr(err, "error enabled foreign_keys") + return nil, errutil.WrapErr(err, "error enabling foreign_keys") } return conn, nil } diff --git a/westport/db/client_test.go b/westport/db/client_test.go new file mode 100644 index 0000000..0d5a1c8 --- /dev/null +++ b/westport/db/client_test.go @@ -0,0 +1,122 @@ +package db + +import ( + "context" + "os" + "path/filepath" + "testing" + + "github.com/sprisa/west/util/ipconv" + entmigrate "github.com/sprisa/west/westport/db/ent/migrate" + "github.com/sprisa/west/westport/db/helpers" +) + +func TestDatastoreType(t *testing.T) { + tests := map[string]string{ + "sqlite": DatastoreSQLite, + "sqlite:///tmp/west.db": DatastoreSQLite, + "postgres://west@localhost/west": DatastorePostgres, + "postgresql://west@localhost/west": DatastorePostgres, + "mysql://west@localhost:3306/west": DatastoreMySQL, + } + for input, want := range tests { + got, err := DatastoreType(input) + if err != nil { + t.Fatalf("DatastoreType(%q): %v", input, err) + } + if got != want { + t.Fatalf("DatastoreType(%q) = %q, want %q", input, got, want) + } + } + if _, err := DatastoreType("cockroach://localhost/west"); err == nil { + t.Fatal("expected unsupported datastore to fail") + } +} + +func TestMySQLDSN(t *testing.T) { + dsn, err := mysqlDSN("mysql://user:p%40ss@db.example:3306/west?tls=true") + if err != nil { + t.Fatal(err) + } + if dsn == "" { + t.Fatal("mysql DSN is empty") + } + if _, err := mysqlDSN("mysql://db.example:3306"); err == nil { + t.Fatal("expected missing database to fail") + } +} + +func TestDatastoreSchema(t *testing.T) { + tests := map[string]string{ + "sqlite": "sqlite://" + filepath.Join(t.TempDir(), "west.db"), + } + if dsn := os.Getenv("WEST_TEST_POSTGRES_DSN"); dsn != "" { + tests["postgres"] = dsn + } + if dsn := os.Getenv("WEST_TEST_MYSQL_DSN"); dsn != "" { + tests["mysql"] = dsn + } + + for name, dataSource := range tests { + t.Run(name, func(t *testing.T) { + ctx := context.Background() + helpers.SetEncryptionPassword([]byte("test-password")) + client, err := OpenDB(ctx, dataSource) + if err != nil { + t.Fatal(err) + } + defer client.Close() + if err := client.Schema.Create(ctx, entmigrate.WithGlobalUniqueID(true)); err != nil { + t.Fatal(err) + } + if _, err := client.Lighthouse.Delete().Exec(ctx); err != nil { + t.Fatal(err) + } + if _, err := client.Device.Delete().Exec(ctx); err != nil { + t.Fatal(err) + } + if _, err := client.Host.Delete().Exec(ctx); err != nil { + t.Fatal(err) + } + if _, err := client.Settings.Delete().Exec(ctx); err != nil { + t.Fatal(err) + } + cidr, err := helpers.NewIpCidr("10.10.10.1/24") + if err != nil { + t.Fatal(err) + } + if err := client.Settings.Create(). + SetCaCrt([]byte("ca")). + SetCaKey([]byte("key")). + SetCidr(cidr). + Exec(ctx); err != nil { + t.Fatal(err) + } + ip, err := ipconv.FromIPAddr(cidr.Addr()) + if err != nil { + t.Fatal(err) + } + host, err := client.Host.Create().SetIP(ip).Save(ctx) + if err != nil { + t.Fatal(err) + } + if err := client.Lighthouse.Create(). + SetIP(ip). + SetEndpoint("127.0.0.1:4242"). + SetAPIEndpoint("127.0.0.1:80"). + SetCertificate([]byte("cert")). + SetKey([]byte("key")). + SetHostID(host.ID). + Exec(ctx); err != nil { + t.Fatal(err) + } + settings, err := client.Settings.Query().Only(ctx) + if err != nil { + t.Fatal(err) + } + if settings.Cidr.String() != cidr.String() { + t.Fatalf("cidr = %q, want %q", settings.Cidr, cidr) + } + }) + } +} diff --git a/westport/db/ent/client.go b/westport/db/ent/client.go index a737edd..4e37842 100644 --- a/westport/db/ent/client.go +++ b/westport/db/ent/client.go @@ -14,7 +14,10 @@ import ( "entgo.io/ent" "entgo.io/ent/dialect" "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" "github.com/sprisa/west/westport/db/ent/device" + "github.com/sprisa/west/westport/db/ent/host" + "github.com/sprisa/west/westport/db/ent/lighthouse" "github.com/sprisa/west/westport/db/ent/settings" ) @@ -25,6 +28,10 @@ type Client struct { Schema *migrate.Schema // Device is the client for interacting with the Device builders. Device *DeviceClient + // Host is the client for interacting with the Host builders. + Host *HostClient + // Lighthouse is the client for interacting with the Lighthouse builders. + Lighthouse *LighthouseClient // Settings is the client for interacting with the Settings builders. Settings *SettingsClient // additional fields for node api @@ -41,6 +48,8 @@ func NewClient(opts ...Option) *Client { func (c *Client) init() { c.Schema = migrate.NewSchema(c.driver) c.Device = NewDeviceClient(c.config) + c.Host = NewHostClient(c.config) + c.Lighthouse = NewLighthouseClient(c.config) c.Settings = NewSettingsClient(c.config) } @@ -132,10 +141,12 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) { cfg := c.config cfg.driver = tx return &Tx{ - ctx: ctx, - config: cfg, - Device: NewDeviceClient(cfg), - Settings: NewSettingsClient(cfg), + ctx: ctx, + config: cfg, + Device: NewDeviceClient(cfg), + Host: NewHostClient(cfg), + Lighthouse: NewLighthouseClient(cfg), + Settings: NewSettingsClient(cfg), }, nil } @@ -153,10 +164,12 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) cfg := c.config cfg.driver = &txDriver{tx: tx, drv: c.driver} return &Tx{ - ctx: ctx, - config: cfg, - Device: NewDeviceClient(cfg), - Settings: NewSettingsClient(cfg), + ctx: ctx, + config: cfg, + Device: NewDeviceClient(cfg), + Host: NewHostClient(cfg), + Lighthouse: NewLighthouseClient(cfg), + Settings: NewSettingsClient(cfg), }, nil } @@ -186,6 +199,8 @@ func (c *Client) Close() error { // In order to add hooks to a specific client, call: `client.Node.Use(...)`. func (c *Client) Use(hooks ...Hook) { c.Device.Use(hooks...) + c.Host.Use(hooks...) + c.Lighthouse.Use(hooks...) c.Settings.Use(hooks...) } @@ -193,6 +208,8 @@ func (c *Client) Use(hooks ...Hook) { // In order to add interceptors to a specific client, call: `client.Node.Intercept(...)`. func (c *Client) Intercept(interceptors ...Interceptor) { c.Device.Intercept(interceptors...) + c.Host.Intercept(interceptors...) + c.Lighthouse.Intercept(interceptors...) c.Settings.Intercept(interceptors...) } @@ -201,6 +218,10 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) { switch m := m.(type) { case *DeviceMutation: return c.Device.mutate(ctx, m) + case *HostMutation: + return c.Host.mutate(ctx, m) + case *LighthouseMutation: + return c.Lighthouse.mutate(ctx, m) case *SettingsMutation: return c.Settings.mutate(ctx, m) default: @@ -316,6 +337,22 @@ func (c *DeviceClient) GetX(ctx context.Context, id int) *Device { return obj } +// QueryHost queries the host edge of a Device. +func (c *DeviceClient) QueryHost(_m *Device) *HostQuery { + query := (&HostClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(device.Table, device.FieldID, id), + sqlgraph.To(host.Table, host.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, device.HostTable, device.HostColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + // Hooks returns the client hooks. func (c *DeviceClient) Hooks() []Hook { return c.hooks.Device @@ -341,6 +378,288 @@ func (c *DeviceClient) mutate(ctx context.Context, m *DeviceMutation) (Value, er } } +// HostClient is a client for the Host schema. +type HostClient struct { + config +} + +// NewHostClient returns a client for the Host from the given config. +func NewHostClient(c config) *HostClient { + return &HostClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `host.Hooks(f(g(h())))`. +func (c *HostClient) Use(hooks ...Hook) { + c.hooks.Host = append(c.hooks.Host, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `host.Intercept(f(g(h())))`. +func (c *HostClient) Intercept(interceptors ...Interceptor) { + c.inters.Host = append(c.inters.Host, interceptors...) +} + +// Create returns a builder for creating a Host entity. +func (c *HostClient) Create() *HostCreate { + mutation := newHostMutation(c.config, OpCreate) + return &HostCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of Host entities. +func (c *HostClient) CreateBulk(builders ...*HostCreate) *HostCreateBulk { + return &HostCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *HostClient) MapCreateBulk(slice any, setFunc func(*HostCreate, int)) *HostCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &HostCreateBulk{err: fmt.Errorf("calling to HostClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*HostCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &HostCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for Host. +func (c *HostClient) Update() *HostUpdate { + mutation := newHostMutation(c.config, OpUpdate) + return &HostUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *HostClient) UpdateOne(_m *Host) *HostUpdateOne { + mutation := newHostMutation(c.config, OpUpdateOne, withHost(_m)) + return &HostUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *HostClient) UpdateOneID(id int) *HostUpdateOne { + mutation := newHostMutation(c.config, OpUpdateOne, withHostID(id)) + return &HostUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for Host. +func (c *HostClient) Delete() *HostDelete { + mutation := newHostMutation(c.config, OpDelete) + return &HostDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *HostClient) DeleteOne(_m *Host) *HostDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *HostClient) DeleteOneID(id int) *HostDeleteOne { + builder := c.Delete().Where(host.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &HostDeleteOne{builder} +} + +// Query returns a query builder for Host. +func (c *HostClient) Query() *HostQuery { + return &HostQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeHost}, + inters: c.Interceptors(), + } +} + +// Get returns a Host entity by its id. +func (c *HostClient) Get(ctx context.Context, id int) (*Host, error) { + return c.Query().Where(host.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *HostClient) GetX(ctx context.Context, id int) *Host { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *HostClient) Hooks() []Hook { + return c.hooks.Host +} + +// Interceptors returns the client interceptors. +func (c *HostClient) Interceptors() []Interceptor { + return c.inters.Host +} + +func (c *HostClient) mutate(ctx context.Context, m *HostMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&HostCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&HostUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&HostUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&HostDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown Host mutation op: %q", m.Op()) + } +} + +// LighthouseClient is a client for the Lighthouse schema. +type LighthouseClient struct { + config +} + +// NewLighthouseClient returns a client for the Lighthouse from the given config. +func NewLighthouseClient(c config) *LighthouseClient { + return &LighthouseClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `lighthouse.Hooks(f(g(h())))`. +func (c *LighthouseClient) Use(hooks ...Hook) { + c.hooks.Lighthouse = append(c.hooks.Lighthouse, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `lighthouse.Intercept(f(g(h())))`. +func (c *LighthouseClient) Intercept(interceptors ...Interceptor) { + c.inters.Lighthouse = append(c.inters.Lighthouse, interceptors...) +} + +// Create returns a builder for creating a Lighthouse entity. +func (c *LighthouseClient) Create() *LighthouseCreate { + mutation := newLighthouseMutation(c.config, OpCreate) + return &LighthouseCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of Lighthouse entities. +func (c *LighthouseClient) CreateBulk(builders ...*LighthouseCreate) *LighthouseCreateBulk { + return &LighthouseCreateBulk{config: c.config, builders: builders} +} + +// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates +// a builder and applies setFunc on it. +func (c *LighthouseClient) MapCreateBulk(slice any, setFunc func(*LighthouseCreate, int)) *LighthouseCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &LighthouseCreateBulk{err: fmt.Errorf("calling to LighthouseClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*LighthouseCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &LighthouseCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for Lighthouse. +func (c *LighthouseClient) Update() *LighthouseUpdate { + mutation := newLighthouseMutation(c.config, OpUpdate) + return &LighthouseUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *LighthouseClient) UpdateOne(_m *Lighthouse) *LighthouseUpdateOne { + mutation := newLighthouseMutation(c.config, OpUpdateOne, withLighthouse(_m)) + return &LighthouseUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *LighthouseClient) UpdateOneID(id int) *LighthouseUpdateOne { + mutation := newLighthouseMutation(c.config, OpUpdateOne, withLighthouseID(id)) + return &LighthouseUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for Lighthouse. +func (c *LighthouseClient) Delete() *LighthouseDelete { + mutation := newLighthouseMutation(c.config, OpDelete) + return &LighthouseDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *LighthouseClient) DeleteOne(_m *Lighthouse) *LighthouseDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *LighthouseClient) DeleteOneID(id int) *LighthouseDeleteOne { + builder := c.Delete().Where(lighthouse.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &LighthouseDeleteOne{builder} +} + +// Query returns a query builder for Lighthouse. +func (c *LighthouseClient) Query() *LighthouseQuery { + return &LighthouseQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeLighthouse}, + inters: c.Interceptors(), + } +} + +// Get returns a Lighthouse entity by its id. +func (c *LighthouseClient) Get(ctx context.Context, id int) (*Lighthouse, error) { + return c.Query().Where(lighthouse.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *LighthouseClient) GetX(ctx context.Context, id int) *Lighthouse { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryHost queries the host edge of a Lighthouse. +func (c *LighthouseClient) QueryHost(_m *Lighthouse) *HostQuery { + query := (&HostClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(lighthouse.Table, lighthouse.FieldID, id), + sqlgraph.To(host.Table, host.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, lighthouse.HostTable, lighthouse.HostColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *LighthouseClient) Hooks() []Hook { + return c.hooks.Lighthouse +} + +// Interceptors returns the client interceptors. +func (c *LighthouseClient) Interceptors() []Interceptor { + return c.inters.Lighthouse +} + +func (c *LighthouseClient) mutate(ctx context.Context, m *LighthouseMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&LighthouseCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&LighthouseUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&LighthouseUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&LighthouseDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown Lighthouse mutation op: %q", m.Op()) + } +} + // SettingsClient is a client for the Settings schema. type SettingsClient struct { config @@ -477,9 +796,9 @@ func (c *SettingsClient) mutate(ctx context.Context, m *SettingsMutation) (Value // hooks and interceptors per client, for fast access. type ( hooks struct { - Device, Settings []ent.Hook + Device, Host, Lighthouse, Settings []ent.Hook } inters struct { - Device, Settings []ent.Interceptor + Device, Host, Lighthouse, Settings []ent.Interceptor } ) diff --git a/westport/db/ent/device.go b/westport/db/ent/device.go index d11b3eb..5573b75 100644 --- a/westport/db/ent/device.go +++ b/westport/db/ent/device.go @@ -11,6 +11,7 @@ import ( "entgo.io/ent/dialect/sql" "github.com/sprisa/west/util/ipconv" "github.com/sprisa/west/westport/db/ent/device" + "github.com/sprisa/west/westport/db/ent/host" "github.com/sprisa/west/westport/db/helpers" ) @@ -30,10 +31,34 @@ type Device struct { // Access Token leased to a provisioned device. Can only issue 1 at a time, similar to a lock. Used to verify only 1 instance of the Device is running. LeasedAccessToken *string `json:"-"` // Token holds the value of the "token" field. - Token helpers.EncryptedBytes `json:"-"` + Token helpers.EncryptedBytes `json:"-"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the DeviceQuery when eager-loading is set. + Edges DeviceEdges `json:"edges"` + device_host *int selectValues sql.SelectValues } +// DeviceEdges holds the relations/edges for other nodes in the graph. +type DeviceEdges struct { + // Host holds the value of the host edge. + Host *Host `json:"host,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [1]bool +} + +// HostOrErr returns the Host value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e DeviceEdges) HostOrErr() (*Host, error) { + if e.Host != nil { + return e.Host, nil + } else if e.loadedTypes[0] { + return nil, &NotFoundError{label: host.Label} + } + return nil, &NotLoadedError{edge: "host"} +} + // scanValues returns the types for scanning values from sql.Rows. func (*Device) scanValues(columns []string) ([]any, error) { values := make([]any, len(columns)) @@ -47,6 +72,8 @@ func (*Device) scanValues(columns []string) ([]any, error) { values[i] = new(sql.NullString) case device.FieldCreatedTime, device.FieldUpdatedTime: values[i] = new(sql.NullTime) + case device.ForeignKeys[0]: // device_host + values[i] = new(sql.NullInt64) default: values[i] = new(sql.UnknownType) } @@ -105,6 +132,13 @@ func (_m *Device) assignValues(columns []string, values []any) error { } else if value != nil { _m.Token = *value } + case device.ForeignKeys[0]: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field device_host", value) + } else if value.Valid { + _m.device_host = new(int) + *_m.device_host = int(value.Int64) + } default: _m.selectValues.Set(columns[i], values[i]) } @@ -118,6 +152,11 @@ func (_m *Device) Value(name string) (ent.Value, error) { return _m.selectValues.Get(name) } +// QueryHost queries the "host" edge of the Device entity. +func (_m *Device) QueryHost() *HostQuery { + return NewDeviceClient(_m.config).QueryHost(_m) +} + // Update returns a builder for updating this Device. // Note that you need to call Device.Unwrap() before calling this method if this Device // was returned from a transaction, and the transaction was committed or rolled back. diff --git a/westport/db/ent/device/device.go b/westport/db/ent/device/device.go index c850464..ee20273 100644 --- a/westport/db/ent/device/device.go +++ b/westport/db/ent/device/device.go @@ -6,6 +6,7 @@ import ( "time" "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" ) const ( @@ -25,8 +26,17 @@ const ( FieldLeasedAccessToken = "leased_access_token" // FieldToken holds the string denoting the token field in the database. FieldToken = "token" + // EdgeHost holds the string denoting the host edge name in mutations. + EdgeHost = "host" // Table holds the table name of the device in the database. Table = "devices" + // HostTable is the table that holds the host relation/edge. + HostTable = "devices" + // HostInverseTable is the table name for the Host entity. + // It exists in this package in order to avoid circular dependency with the "host" package. + HostInverseTable = "hosts" + // HostColumn is the table column denoting the host relation/edge. + HostColumn = "device_host" ) // Columns holds all SQL columns for device fields. @@ -40,6 +50,12 @@ var Columns = []string{ FieldToken, } +// ForeignKeys holds the SQL foreign-keys that are owned by the "devices" +// table and are not defined as standalone fields in the schema. +var ForeignKeys = []string{ + "device_host", +} + // ValidColumn reports if the column name is valid (part of the table columns). func ValidColumn(column string) bool { for i := range Columns { @@ -47,6 +63,11 @@ func ValidColumn(column string) bool { return true } } + for i := range ForeignKeys { + if column == ForeignKeys[i] { + return true + } + } return false } @@ -95,3 +116,17 @@ func ByIP(opts ...sql.OrderTermOption) OrderOption { func ByLeasedAccessToken(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldLeasedAccessToken, opts...).ToFunc() } + +// ByHostField orders the results by host field. +func ByHostField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newHostStep(), sql.OrderByField(field, opts...)) + } +} +func newHostStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(HostInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, HostTable, HostColumn), + ) +} diff --git a/westport/db/ent/device/where.go b/westport/db/ent/device/where.go index 35c3a2d..16e8396 100644 --- a/westport/db/ent/device/where.go +++ b/westport/db/ent/device/where.go @@ -6,6 +6,7 @@ import ( "time" "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" "github.com/sprisa/west/util/ipconv" "github.com/sprisa/west/westport/db/ent/predicate" "github.com/sprisa/west/westport/db/helpers" @@ -401,6 +402,29 @@ func TokenLTE(v helpers.EncryptedBytes) predicate.Device { return predicate.Device(sql.FieldLTE(FieldToken, v)) } +// HasHost applies the HasEdge predicate on the "host" edge. +func HasHost() predicate.Device { + return predicate.Device(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, HostTable, HostColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasHostWith applies the HasEdge predicate on the "host" edge with a given conditions (other predicates). +func HasHostWith(preds ...predicate.Host) predicate.Device { + return predicate.Device(func(s *sql.Selector) { + step := newHostStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + // And groups predicates with the AND operator between them. func And(predicates ...predicate.Device) predicate.Device { return predicate.Device(sql.AndPredicates(predicates...)) diff --git a/westport/db/ent/device_create.go b/westport/db/ent/device_create.go index 456701c..eec0b84 100644 --- a/westport/db/ent/device_create.go +++ b/westport/db/ent/device_create.go @@ -12,6 +12,7 @@ import ( "entgo.io/ent/schema/field" "github.com/sprisa/west/util/ipconv" "github.com/sprisa/west/westport/db/ent/device" + "github.com/sprisa/west/westport/db/ent/host" "github.com/sprisa/west/westport/db/helpers" ) @@ -90,6 +91,17 @@ func (_c *DeviceCreate) SetToken(v helpers.EncryptedBytes) *DeviceCreate { return _c } +// SetHostID sets the "host" edge to the Host entity by ID. +func (_c *DeviceCreate) SetHostID(id int) *DeviceCreate { + _c.mutation.SetHostID(id) + return _c +} + +// SetHost sets the "host" edge to the Host entity. +func (_c *DeviceCreate) SetHost(v *Host) *DeviceCreate { + return _c.SetHostID(v.ID) +} + // Mutation returns the DeviceMutation object of the builder. func (_c *DeviceCreate) Mutation() *DeviceMutation { return _c.mutation @@ -161,6 +173,9 @@ func (_c *DeviceCreate) check() error { if _, ok := _c.mutation.Token(); !ok { return &ValidationError{Name: "token", err: errors.New(`ent: missing required field "Device.token"`)} } + if len(_c.mutation.HostIDs()) == 0 { + return &ValidationError{Name: "host", err: errors.New(`ent: missing required edge "Device.host"`)} + } return nil } @@ -211,6 +226,23 @@ func (_c *DeviceCreate) createSpec() (*Device, *sqlgraph.CreateSpec) { _spec.SetField(device.FieldToken, field.TypeBytes, value) _node.Token = value } + if nodes := _c.mutation.HostIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: device.HostTable, + Columns: []string{device.HostColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(host.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.device_host = &nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } return _node, _spec } diff --git a/westport/db/ent/device_query.go b/westport/db/ent/device_query.go index c1fbeb1..1989172 100644 --- a/westport/db/ent/device_query.go +++ b/westport/db/ent/device_query.go @@ -12,6 +12,7 @@ import ( "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" "github.com/sprisa/west/westport/db/ent/device" + "github.com/sprisa/west/westport/db/ent/host" "github.com/sprisa/west/westport/db/ent/predicate" ) @@ -22,6 +23,8 @@ type DeviceQuery struct { order []device.OrderOption inters []Interceptor predicates []predicate.Device + withHost *HostQuery + withFKs bool modifiers []func(*sql.Selector) loadTotal []func(context.Context, []*Device) error // intermediate query (i.e. traversal path). @@ -60,6 +63,28 @@ func (_q *DeviceQuery) Order(o ...device.OrderOption) *DeviceQuery { return _q } +// QueryHost chains the current query on the "host" edge. +func (_q *DeviceQuery) QueryHost() *HostQuery { + query := (&HostClient{config: _q.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + selector := _q.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(device.Table, device.FieldID, selector), + sqlgraph.To(host.Table, host.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, device.HostTable, device.HostColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + // First returns the first Device entity from the query. // Returns a *NotFoundError when no Device was found. func (_q *DeviceQuery) First(ctx context.Context) (*Device, error) { @@ -252,12 +277,24 @@ func (_q *DeviceQuery) Clone() *DeviceQuery { order: append([]device.OrderOption{}, _q.order...), inters: append([]Interceptor{}, _q.inters...), predicates: append([]predicate.Device{}, _q.predicates...), + withHost: _q.withHost.Clone(), // clone intermediate query. sql: _q.sql.Clone(), path: _q.path, } } +// WithHost tells the query-builder to eager-load the nodes that are connected to +// the "host" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *DeviceQuery) WithHost(opts ...func(*HostQuery)) *DeviceQuery { + query := (&HostClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withHost = query + return _q +} + // GroupBy is used to group vertices by one or more fields/columns. // It is often used with aggregate functions, like: count, max, mean, min, sum. // @@ -334,15 +371,26 @@ func (_q *DeviceQuery) prepareQuery(ctx context.Context) error { func (_q *DeviceQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Device, error) { var ( - nodes = []*Device{} - _spec = _q.querySpec() + nodes = []*Device{} + withFKs = _q.withFKs + _spec = _q.querySpec() + loadedTypes = [1]bool{ + _q.withHost != nil, + } ) + if _q.withHost != nil { + withFKs = true + } + if withFKs { + _spec.Node.Columns = append(_spec.Node.Columns, device.ForeignKeys...) + } _spec.ScanValues = func(columns []string) ([]any, error) { return (*Device).scanValues(nil, columns) } _spec.Assign = func(columns []string, values []any) error { node := &Device{config: _q.config} nodes = append(nodes, node) + node.Edges.loadedTypes = loadedTypes return node.assignValues(columns, values) } if len(_q.modifiers) > 0 { @@ -357,6 +405,12 @@ func (_q *DeviceQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Devic if len(nodes) == 0 { return nodes, nil } + if query := _q.withHost; query != nil { + if err := _q.loadHost(ctx, query, nodes, nil, + func(n *Device, e *Host) { n.Edges.Host = e }); err != nil { + return nil, err + } + } for i := range _q.loadTotal { if err := _q.loadTotal[i](ctx, nodes); err != nil { return nil, err @@ -365,6 +419,39 @@ func (_q *DeviceQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Devic return nodes, nil } +func (_q *DeviceQuery) loadHost(ctx context.Context, query *HostQuery, nodes []*Device, init func(*Device), assign func(*Device, *Host)) error { + ids := make([]int, 0, len(nodes)) + nodeids := make(map[int][]*Device) + for i := range nodes { + if nodes[i].device_host == nil { + continue + } + fk := *nodes[i].device_host + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(host.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return fmt.Errorf(`unexpected foreign-key "device_host" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} + func (_q *DeviceQuery) sqlCount(ctx context.Context) (int, error) { _spec := _q.querySpec() if len(_q.modifiers) > 0 { diff --git a/westport/db/ent/device_update.go b/westport/db/ent/device_update.go index 6abbee0..e79573f 100644 --- a/westport/db/ent/device_update.go +++ b/westport/db/ent/device_update.go @@ -12,6 +12,7 @@ import ( "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" "github.com/sprisa/west/westport/db/ent/device" + "github.com/sprisa/west/westport/db/ent/host" "github.com/sprisa/west/westport/db/ent/predicate" "github.com/sprisa/west/westport/db/helpers" ) @@ -75,11 +76,28 @@ func (_u *DeviceUpdate) SetToken(v helpers.EncryptedBytes) *DeviceUpdate { return _u } +// SetHostID sets the "host" edge to the Host entity by ID. +func (_u *DeviceUpdate) SetHostID(id int) *DeviceUpdate { + _u.mutation.SetHostID(id) + return _u +} + +// SetHost sets the "host" edge to the Host entity. +func (_u *DeviceUpdate) SetHost(v *Host) *DeviceUpdate { + return _u.SetHostID(v.ID) +} + // Mutation returns the DeviceMutation object of the builder. func (_u *DeviceUpdate) Mutation() *DeviceMutation { return _u.mutation } +// ClearHost clears the "host" edge to the Host entity. +func (_u *DeviceUpdate) ClearHost() *DeviceUpdate { + _u.mutation.ClearHost() + return _u +} + // Save executes the query and returns the number of nodes affected by the update operation. func (_u *DeviceUpdate) Save(ctx context.Context) (int, error) { _u.defaults() @@ -116,7 +134,18 @@ func (_u *DeviceUpdate) defaults() { } } +// check runs all checks and user-defined validators on the builder. +func (_u *DeviceUpdate) check() error { + if _u.mutation.HostCleared() && len(_u.mutation.HostIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "Device.host"`) + } + return nil +} + func (_u *DeviceUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } _spec := sqlgraph.NewUpdateSpec(device.Table, device.Columns, sqlgraph.NewFieldSpec(device.FieldID, field.TypeInt)) if ps := _u.mutation.predicates; len(ps) > 0 { _spec.Predicate = func(selector *sql.Selector) { @@ -140,6 +169,35 @@ func (_u *DeviceUpdate) sqlSave(ctx context.Context) (_node int, err error) { if value, ok := _u.mutation.Token(); ok { _spec.SetField(device.FieldToken, field.TypeBytes, value) } + if _u.mutation.HostCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: device.HostTable, + Columns: []string{device.HostColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(host.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.HostIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: device.HostTable, + Columns: []string{device.HostColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(host.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { if _, ok := err.(*sqlgraph.NotFoundError); ok { err = &NotFoundError{device.Label} @@ -206,11 +264,28 @@ func (_u *DeviceUpdateOne) SetToken(v helpers.EncryptedBytes) *DeviceUpdateOne { return _u } +// SetHostID sets the "host" edge to the Host entity by ID. +func (_u *DeviceUpdateOne) SetHostID(id int) *DeviceUpdateOne { + _u.mutation.SetHostID(id) + return _u +} + +// SetHost sets the "host" edge to the Host entity. +func (_u *DeviceUpdateOne) SetHost(v *Host) *DeviceUpdateOne { + return _u.SetHostID(v.ID) +} + // Mutation returns the DeviceMutation object of the builder. func (_u *DeviceUpdateOne) Mutation() *DeviceMutation { return _u.mutation } +// ClearHost clears the "host" edge to the Host entity. +func (_u *DeviceUpdateOne) ClearHost() *DeviceUpdateOne { + _u.mutation.ClearHost() + return _u +} + // Where appends a list predicates to the DeviceUpdate builder. func (_u *DeviceUpdateOne) Where(ps ...predicate.Device) *DeviceUpdateOne { _u.mutation.Where(ps...) @@ -260,7 +335,18 @@ func (_u *DeviceUpdateOne) defaults() { } } +// check runs all checks and user-defined validators on the builder. +func (_u *DeviceUpdateOne) check() error { + if _u.mutation.HostCleared() && len(_u.mutation.HostIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "Device.host"`) + } + return nil +} + func (_u *DeviceUpdateOne) sqlSave(ctx context.Context) (_node *Device, err error) { + if err := _u.check(); err != nil { + return _node, err + } _spec := sqlgraph.NewUpdateSpec(device.Table, device.Columns, sqlgraph.NewFieldSpec(device.FieldID, field.TypeInt)) id, ok := _u.mutation.ID() if !ok { @@ -301,6 +387,35 @@ func (_u *DeviceUpdateOne) sqlSave(ctx context.Context) (_node *Device, err erro if value, ok := _u.mutation.Token(); ok { _spec.SetField(device.FieldToken, field.TypeBytes, value) } + if _u.mutation.HostCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: device.HostTable, + Columns: []string{device.HostColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(host.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.HostIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: device.HostTable, + Columns: []string{device.HostColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(host.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } _node = &Device{config: _u.config} _spec.Assign = _node.assignValues _spec.ScanValues = _node.scanValues diff --git a/westport/db/ent/ent.go b/westport/db/ent/ent.go index 032d749..c15d12d 100644 --- a/westport/db/ent/ent.go +++ b/westport/db/ent/ent.go @@ -13,6 +13,8 @@ import ( "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "github.com/sprisa/west/westport/db/ent/device" + "github.com/sprisa/west/westport/db/ent/host" + "github.com/sprisa/west/westport/db/ent/lighthouse" "github.com/sprisa/west/westport/db/ent/settings" ) @@ -74,8 +76,10 @@ var ( func checkColumn(t, c string) error { initCheck.Do(func() { columnCheck = sql.NewColumnCheck(map[string]func(string) bool{ - device.Table: device.ValidColumn, - settings.Table: settings.ValidColumn, + device.Table: device.ValidColumn, + host.Table: host.ValidColumn, + lighthouse.Table: lighthouse.ValidColumn, + settings.Table: settings.ValidColumn, }) }) return columnCheck(t, c) diff --git a/westport/db/ent/entql.go b/westport/db/ent/entql.go index 0a25a0d..95bef95 100644 --- a/westport/db/ent/entql.go +++ b/westport/db/ent/entql.go @@ -4,6 +4,9 @@ package ent import ( "github.com/sprisa/west/westport/db/ent/device" + "github.com/sprisa/west/westport/db/ent/host" + "github.com/sprisa/west/westport/db/ent/lighthouse" + "github.com/sprisa/west/westport/db/ent/predicate" "github.com/sprisa/west/westport/db/ent/settings" "entgo.io/ent/dialect/sql" @@ -14,7 +17,7 @@ import ( // schemaGraph holds a representation of ent/schema at runtime. var schemaGraph = func() *sqlgraph.Schema { - graph := &sqlgraph.Schema{Nodes: make([]*sqlgraph.Node, 2)} + graph := &sqlgraph.Schema{Nodes: make([]*sqlgraph.Node, 4)} graph.Nodes[0] = &sqlgraph.Node{ NodeSpec: sqlgraph.NodeSpec{ Table: device.Table, @@ -35,6 +38,42 @@ var schemaGraph = func() *sqlgraph.Schema { }, } graph.Nodes[1] = &sqlgraph.Node{ + NodeSpec: sqlgraph.NodeSpec{ + Table: host.Table, + Columns: host.Columns, + ID: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: host.FieldID, + }, + }, + Type: "Host", + Fields: map[string]*sqlgraph.FieldSpec{ + host.FieldCreatedTime: {Type: field.TypeTime, Column: host.FieldCreatedTime}, + host.FieldUpdatedTime: {Type: field.TypeTime, Column: host.FieldUpdatedTime}, + host.FieldIP: {Type: field.TypeUint32, Column: host.FieldIP}, + }, + } + graph.Nodes[2] = &sqlgraph.Node{ + NodeSpec: sqlgraph.NodeSpec{ + Table: lighthouse.Table, + Columns: lighthouse.Columns, + ID: &sqlgraph.FieldSpec{ + Type: field.TypeInt, + Column: lighthouse.FieldID, + }, + }, + Type: "Lighthouse", + Fields: map[string]*sqlgraph.FieldSpec{ + lighthouse.FieldCreatedTime: {Type: field.TypeTime, Column: lighthouse.FieldCreatedTime}, + lighthouse.FieldUpdatedTime: {Type: field.TypeTime, Column: lighthouse.FieldUpdatedTime}, + lighthouse.FieldIP: {Type: field.TypeUint32, Column: lighthouse.FieldIP}, + lighthouse.FieldEndpoint: {Type: field.TypeString, Column: lighthouse.FieldEndpoint}, + lighthouse.FieldCertificate: {Type: field.TypeBytes, Column: lighthouse.FieldCertificate}, + lighthouse.FieldKey: {Type: field.TypeBytes, Column: lighthouse.FieldKey}, + lighthouse.FieldAPIEndpoint: {Type: field.TypeString, Column: lighthouse.FieldAPIEndpoint}, + }, + } + graph.Nodes[3] = &sqlgraph.Node{ NodeSpec: sqlgraph.NodeSpec{ Table: settings.Table, Columns: settings.Columns, @@ -47,19 +86,41 @@ var schemaGraph = func() *sqlgraph.Schema { Fields: map[string]*sqlgraph.FieldSpec{ settings.FieldCreatedTime: {Type: field.TypeTime, Column: settings.FieldCreatedTime}, settings.FieldUpdatedTime: {Type: field.TypeTime, Column: settings.FieldUpdatedTime}, + settings.FieldNetworkID: {Type: field.TypeString, Column: settings.FieldNetworkID}, settings.FieldDomainZone: {Type: field.TypeString, Column: settings.FieldDomainZone}, settings.FieldCipher: {Type: field.TypeString, Column: settings.FieldCipher}, settings.FieldCaCrt: {Type: field.TypeBytes, Column: settings.FieldCaCrt}, settings.FieldCaKey: {Type: field.TypeBytes, Column: settings.FieldCaKey}, - settings.FieldLighthouseCrt: {Type: field.TypeBytes, Column: settings.FieldLighthouseCrt}, - settings.FieldLighthouseKey: {Type: field.TypeBytes, Column: settings.FieldLighthouseKey}, settings.FieldCidr: {Type: field.TypeString, Column: settings.FieldCidr}, - settings.FieldPortOverlayIP: {Type: field.TypeUint32, Column: settings.FieldPortOverlayIP}, settings.FieldLetsencryptRegistration: {Type: field.TypeBytes, Column: settings.FieldLetsencryptRegistration}, settings.FieldTLSCert: {Type: field.TypeBytes, Column: settings.FieldTLSCert}, settings.FieldTLSCertKey: {Type: field.TypeBytes, Column: settings.FieldTLSCertKey}, }, } + graph.MustAddE( + "host", + &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: device.HostTable, + Columns: []string{device.HostColumn}, + Bidi: false, + }, + "Device", + "Host", + ) + graph.MustAddE( + "host", + &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: lighthouse.HostTable, + Columns: []string{lighthouse.HostColumn}, + Bidi: false, + }, + "Lighthouse", + "Host", + ) return graph }() @@ -139,6 +200,164 @@ func (f *DeviceFilter) WhereToken(p entql.BytesP) { f.Where(p.Field(device.FieldToken)) } +// WhereHasHost applies a predicate to check if query has an edge host. +func (f *DeviceFilter) WhereHasHost() { + f.Where(entql.HasEdge("host")) +} + +// WhereHasHostWith applies a predicate to check if query has an edge host with a given conditions (other predicates). +func (f *DeviceFilter) WhereHasHostWith(preds ...predicate.Host) { + f.Where(entql.HasEdgeWith("host", sqlgraph.WrapFunc(func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }))) +} + +// addPredicate implements the predicateAdder interface. +func (_q *HostQuery) addPredicate(pred func(s *sql.Selector)) { + _q.predicates = append(_q.predicates, pred) +} + +// Filter returns a Filter implementation to apply filters on the HostQuery builder. +func (_q *HostQuery) Filter() *HostFilter { + return &HostFilter{config: _q.config, predicateAdder: _q} +} + +// addPredicate implements the predicateAdder interface. +func (m *HostMutation) addPredicate(pred func(s *sql.Selector)) { + m.predicates = append(m.predicates, pred) +} + +// Filter returns an entql.Where implementation to apply filters on the HostMutation builder. +func (m *HostMutation) Filter() *HostFilter { + return &HostFilter{config: m.config, predicateAdder: m} +} + +// HostFilter provides a generic filtering capability at runtime for HostQuery. +type HostFilter struct { + predicateAdder + config +} + +// Where applies the entql predicate on the query filter. +func (f *HostFilter) Where(p entql.P) { + f.addPredicate(func(s *sql.Selector) { + if err := schemaGraph.EvalP(schemaGraph.Nodes[1].Type, p, s); err != nil { + s.AddError(err) + } + }) +} + +// WhereID applies the entql int predicate on the id field. +func (f *HostFilter) WhereID(p entql.IntP) { + f.Where(p.Field(host.FieldID)) +} + +// WhereCreatedTime applies the entql time.Time predicate on the created_time field. +func (f *HostFilter) WhereCreatedTime(p entql.TimeP) { + f.Where(p.Field(host.FieldCreatedTime)) +} + +// WhereUpdatedTime applies the entql time.Time predicate on the updated_time field. +func (f *HostFilter) WhereUpdatedTime(p entql.TimeP) { + f.Where(p.Field(host.FieldUpdatedTime)) +} + +// WhereIP applies the entql uint32 predicate on the ip field. +func (f *HostFilter) WhereIP(p entql.Uint32P) { + f.Where(p.Field(host.FieldIP)) +} + +// addPredicate implements the predicateAdder interface. +func (_q *LighthouseQuery) addPredicate(pred func(s *sql.Selector)) { + _q.predicates = append(_q.predicates, pred) +} + +// Filter returns a Filter implementation to apply filters on the LighthouseQuery builder. +func (_q *LighthouseQuery) Filter() *LighthouseFilter { + return &LighthouseFilter{config: _q.config, predicateAdder: _q} +} + +// addPredicate implements the predicateAdder interface. +func (m *LighthouseMutation) addPredicate(pred func(s *sql.Selector)) { + m.predicates = append(m.predicates, pred) +} + +// Filter returns an entql.Where implementation to apply filters on the LighthouseMutation builder. +func (m *LighthouseMutation) Filter() *LighthouseFilter { + return &LighthouseFilter{config: m.config, predicateAdder: m} +} + +// LighthouseFilter provides a generic filtering capability at runtime for LighthouseQuery. +type LighthouseFilter struct { + predicateAdder + config +} + +// Where applies the entql predicate on the query filter. +func (f *LighthouseFilter) Where(p entql.P) { + f.addPredicate(func(s *sql.Selector) { + if err := schemaGraph.EvalP(schemaGraph.Nodes[2].Type, p, s); err != nil { + s.AddError(err) + } + }) +} + +// WhereID applies the entql int predicate on the id field. +func (f *LighthouseFilter) WhereID(p entql.IntP) { + f.Where(p.Field(lighthouse.FieldID)) +} + +// WhereCreatedTime applies the entql time.Time predicate on the created_time field. +func (f *LighthouseFilter) WhereCreatedTime(p entql.TimeP) { + f.Where(p.Field(lighthouse.FieldCreatedTime)) +} + +// WhereUpdatedTime applies the entql time.Time predicate on the updated_time field. +func (f *LighthouseFilter) WhereUpdatedTime(p entql.TimeP) { + f.Where(p.Field(lighthouse.FieldUpdatedTime)) +} + +// WhereIP applies the entql uint32 predicate on the ip field. +func (f *LighthouseFilter) WhereIP(p entql.Uint32P) { + f.Where(p.Field(lighthouse.FieldIP)) +} + +// WhereEndpoint applies the entql string predicate on the endpoint field. +func (f *LighthouseFilter) WhereEndpoint(p entql.StringP) { + f.Where(p.Field(lighthouse.FieldEndpoint)) +} + +// WhereCertificate applies the entql []byte predicate on the certificate field. +func (f *LighthouseFilter) WhereCertificate(p entql.BytesP) { + f.Where(p.Field(lighthouse.FieldCertificate)) +} + +// WhereKey applies the entql []byte predicate on the key field. +func (f *LighthouseFilter) WhereKey(p entql.BytesP) { + f.Where(p.Field(lighthouse.FieldKey)) +} + +// WhereAPIEndpoint applies the entql string predicate on the api_endpoint field. +func (f *LighthouseFilter) WhereAPIEndpoint(p entql.StringP) { + f.Where(p.Field(lighthouse.FieldAPIEndpoint)) +} + +// WhereHasHost applies a predicate to check if query has an edge host. +func (f *LighthouseFilter) WhereHasHost() { + f.Where(entql.HasEdge("host")) +} + +// WhereHasHostWith applies a predicate to check if query has an edge host with a given conditions (other predicates). +func (f *LighthouseFilter) WhereHasHostWith(preds ...predicate.Host) { + f.Where(entql.HasEdgeWith("host", sqlgraph.WrapFunc(func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }))) +} + // addPredicate implements the predicateAdder interface. func (_q *SettingsQuery) addPredicate(pred func(s *sql.Selector)) { _q.predicates = append(_q.predicates, pred) @@ -168,7 +387,7 @@ type SettingsFilter struct { // Where applies the entql predicate on the query filter. func (f *SettingsFilter) Where(p entql.P) { f.addPredicate(func(s *sql.Selector) { - if err := schemaGraph.EvalP(schemaGraph.Nodes[1].Type, p, s); err != nil { + if err := schemaGraph.EvalP(schemaGraph.Nodes[3].Type, p, s); err != nil { s.AddError(err) } }) @@ -189,6 +408,11 @@ func (f *SettingsFilter) WhereUpdatedTime(p entql.TimeP) { f.Where(p.Field(settings.FieldUpdatedTime)) } +// WhereNetworkID applies the entql string predicate on the network_id field. +func (f *SettingsFilter) WhereNetworkID(p entql.StringP) { + f.Where(p.Field(settings.FieldNetworkID)) +} + // WhereDomainZone applies the entql string predicate on the domain_zone field. func (f *SettingsFilter) WhereDomainZone(p entql.StringP) { f.Where(p.Field(settings.FieldDomainZone)) @@ -209,26 +433,11 @@ func (f *SettingsFilter) WhereCaKey(p entql.BytesP) { f.Where(p.Field(settings.FieldCaKey)) } -// WhereLighthouseCrt applies the entql []byte predicate on the lighthouse_crt field. -func (f *SettingsFilter) WhereLighthouseCrt(p entql.BytesP) { - f.Where(p.Field(settings.FieldLighthouseCrt)) -} - -// WhereLighthouseKey applies the entql []byte predicate on the lighthouse_key field. -func (f *SettingsFilter) WhereLighthouseKey(p entql.BytesP) { - f.Where(p.Field(settings.FieldLighthouseKey)) -} - // WhereCidr applies the entql string predicate on the cidr field. func (f *SettingsFilter) WhereCidr(p entql.StringP) { f.Where(p.Field(settings.FieldCidr)) } -// WherePortOverlayIP applies the entql uint32 predicate on the port_overlay_ip field. -func (f *SettingsFilter) WherePortOverlayIP(p entql.Uint32P) { - f.Where(p.Field(settings.FieldPortOverlayIP)) -} - // WhereLetsencryptRegistration applies the entql []byte predicate on the letsencrypt_registration field. func (f *SettingsFilter) WhereLetsencryptRegistration(p entql.BytesP) { f.Where(p.Field(settings.FieldLetsencryptRegistration)) diff --git a/westport/db/ent/hook/hook.go b/westport/db/ent/hook/hook.go index c15ce05..9e283a2 100644 --- a/westport/db/ent/hook/hook.go +++ b/westport/db/ent/hook/hook.go @@ -21,6 +21,30 @@ func (f DeviceFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, erro return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.DeviceMutation", m) } +// The HostFunc type is an adapter to allow the use of ordinary +// function as Host mutator. +type HostFunc func(context.Context, *ent.HostMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f HostFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.HostMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.HostMutation", m) +} + +// The LighthouseFunc type is an adapter to allow the use of ordinary +// function as Lighthouse mutator. +type LighthouseFunc func(context.Context, *ent.LighthouseMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f LighthouseFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.LighthouseMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.LighthouseMutation", m) +} + // The SettingsFunc type is an adapter to allow the use of ordinary // function as Settings mutator. type SettingsFunc func(context.Context, *ent.SettingsMutation) (ent.Value, error) diff --git a/westport/db/ent/host.go b/westport/db/ent/host.go new file mode 100644 index 0000000..9c86cf7 --- /dev/null +++ b/westport/db/ent/host.go @@ -0,0 +1,127 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/sprisa/west/util/ipconv" + "github.com/sprisa/west/westport/db/ent/host" +) + +// Host is the model entity for the Host schema. +type Host struct { + config `json:"-"` + // ID of the ent. + ID int `json:"id,omitempty"` + // Time ent was created + CreatedTime time.Time `json:"created_time,omitempty"` + // Time ent was updated + UpdatedTime time.Time `json:"updated_time,omitempty"` + // IP holds the value of the "ip" field. + IP ipconv.IP `json:"ip,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*Host) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case host.FieldID, host.FieldIP: + values[i] = new(sql.NullInt64) + case host.FieldCreatedTime, host.FieldUpdatedTime: + values[i] = new(sql.NullTime) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the Host fields. +func (_m *Host) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case host.FieldID: + value, ok := values[i].(*sql.NullInt64) + if !ok { + return fmt.Errorf("unexpected type %T for field id", value) + } + _m.ID = int(value.Int64) + case host.FieldCreatedTime: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_time", values[i]) + } else if value.Valid { + _m.CreatedTime = value.Time + } + case host.FieldUpdatedTime: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated_time", values[i]) + } else if value.Valid { + _m.UpdatedTime = value.Time + } + case host.FieldIP: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field ip", values[i]) + } else if value.Valid { + _m.IP = ipconv.IP(value.Int64) + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the Host. +// This includes values selected through modifiers, order, etc. +func (_m *Host) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this Host. +// Note that you need to call Host.Unwrap() before calling this method if this Host +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *Host) Update() *HostUpdateOne { + return NewHostClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the Host entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *Host) Unwrap() *Host { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: Host is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *Host) String() string { + var builder strings.Builder + builder.WriteString("Host(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("created_time=") + builder.WriteString(_m.CreatedTime.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated_time=") + builder.WriteString(_m.UpdatedTime.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("ip=") + builder.WriteString(fmt.Sprintf("%v", _m.IP)) + builder.WriteByte(')') + return builder.String() +} + +// Hosts is a parsable slice of Host. +type Hosts []*Host diff --git a/westport/db/ent/host/host.go b/westport/db/ent/host/host.go new file mode 100644 index 0000000..50c165a --- /dev/null +++ b/westport/db/ent/host/host.go @@ -0,0 +1,76 @@ +// Code generated by ent, DO NOT EDIT. + +package host + +import ( + "time" + + "entgo.io/ent/dialect/sql" +) + +const ( + // Label holds the string label denoting the host type in the database. + Label = "host" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldCreatedTime holds the string denoting the created_time field in the database. + FieldCreatedTime = "created_time" + // FieldUpdatedTime holds the string denoting the updated_time field in the database. + FieldUpdatedTime = "updated_time" + // FieldIP holds the string denoting the ip field in the database. + FieldIP = "ip" + // Table holds the table name of the host in the database. + Table = "hosts" +) + +// Columns holds all SQL columns for host fields. +var Columns = []string{ + FieldID, + FieldCreatedTime, + FieldUpdatedTime, + FieldIP, +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + return false +} + +var ( + // DefaultCreatedTime holds the default value on creation for the "created_time" field. + DefaultCreatedTime func() time.Time + // DefaultUpdatedTime holds the default value on creation for the "updated_time" field. + DefaultUpdatedTime func() time.Time + // UpdateDefaultUpdatedTime holds the default value on update for the "updated_time" field. + UpdateDefaultUpdatedTime func() time.Time + // IPValidator is a validator for the "ip" field. It is called by the builders before save. + IPValidator func(uint32) error +) + +// OrderOption defines the ordering options for the Host queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByCreatedTime orders the results by the created_time field. +func ByCreatedTime(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedTime, opts...).ToFunc() +} + +// ByUpdatedTime orders the results by the updated_time field. +func ByUpdatedTime(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedTime, opts...).ToFunc() +} + +// ByIP orders the results by the ip field. +func ByIP(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldIP, opts...).ToFunc() +} diff --git a/westport/db/ent/host/where.go b/westport/db/ent/host/where.go new file mode 100644 index 0000000..caa6379 --- /dev/null +++ b/westport/db/ent/host/where.go @@ -0,0 +1,221 @@ +// Code generated by ent, DO NOT EDIT. + +package host + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/sprisa/west/util/ipconv" + "github.com/sprisa/west/westport/db/ent/predicate" +) + +// ID filters vertices based on their ID field. +func ID(id int) predicate.Host { + return predicate.Host(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id int) predicate.Host { + return predicate.Host(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id int) predicate.Host { + return predicate.Host(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...int) predicate.Host { + return predicate.Host(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...int) predicate.Host { + return predicate.Host(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id int) predicate.Host { + return predicate.Host(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id int) predicate.Host { + return predicate.Host(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id int) predicate.Host { + return predicate.Host(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id int) predicate.Host { + return predicate.Host(sql.FieldLTE(FieldID, id)) +} + +// CreatedTime applies equality check predicate on the "created_time" field. It's identical to CreatedTimeEQ. +func CreatedTime(v time.Time) predicate.Host { + return predicate.Host(sql.FieldEQ(FieldCreatedTime, v)) +} + +// UpdatedTime applies equality check predicate on the "updated_time" field. It's identical to UpdatedTimeEQ. +func UpdatedTime(v time.Time) predicate.Host { + return predicate.Host(sql.FieldEQ(FieldUpdatedTime, v)) +} + +// IP applies equality check predicate on the "ip" field. It's identical to IPEQ. +func IP(v ipconv.IP) predicate.Host { + vc := uint32(v) + return predicate.Host(sql.FieldEQ(FieldIP, vc)) +} + +// CreatedTimeEQ applies the EQ predicate on the "created_time" field. +func CreatedTimeEQ(v time.Time) predicate.Host { + return predicate.Host(sql.FieldEQ(FieldCreatedTime, v)) +} + +// CreatedTimeNEQ applies the NEQ predicate on the "created_time" field. +func CreatedTimeNEQ(v time.Time) predicate.Host { + return predicate.Host(sql.FieldNEQ(FieldCreatedTime, v)) +} + +// CreatedTimeIn applies the In predicate on the "created_time" field. +func CreatedTimeIn(vs ...time.Time) predicate.Host { + return predicate.Host(sql.FieldIn(FieldCreatedTime, vs...)) +} + +// CreatedTimeNotIn applies the NotIn predicate on the "created_time" field. +func CreatedTimeNotIn(vs ...time.Time) predicate.Host { + return predicate.Host(sql.FieldNotIn(FieldCreatedTime, vs...)) +} + +// CreatedTimeGT applies the GT predicate on the "created_time" field. +func CreatedTimeGT(v time.Time) predicate.Host { + return predicate.Host(sql.FieldGT(FieldCreatedTime, v)) +} + +// CreatedTimeGTE applies the GTE predicate on the "created_time" field. +func CreatedTimeGTE(v time.Time) predicate.Host { + return predicate.Host(sql.FieldGTE(FieldCreatedTime, v)) +} + +// CreatedTimeLT applies the LT predicate on the "created_time" field. +func CreatedTimeLT(v time.Time) predicate.Host { + return predicate.Host(sql.FieldLT(FieldCreatedTime, v)) +} + +// CreatedTimeLTE applies the LTE predicate on the "created_time" field. +func CreatedTimeLTE(v time.Time) predicate.Host { + return predicate.Host(sql.FieldLTE(FieldCreatedTime, v)) +} + +// UpdatedTimeEQ applies the EQ predicate on the "updated_time" field. +func UpdatedTimeEQ(v time.Time) predicate.Host { + return predicate.Host(sql.FieldEQ(FieldUpdatedTime, v)) +} + +// UpdatedTimeNEQ applies the NEQ predicate on the "updated_time" field. +func UpdatedTimeNEQ(v time.Time) predicate.Host { + return predicate.Host(sql.FieldNEQ(FieldUpdatedTime, v)) +} + +// UpdatedTimeIn applies the In predicate on the "updated_time" field. +func UpdatedTimeIn(vs ...time.Time) predicate.Host { + return predicate.Host(sql.FieldIn(FieldUpdatedTime, vs...)) +} + +// UpdatedTimeNotIn applies the NotIn predicate on the "updated_time" field. +func UpdatedTimeNotIn(vs ...time.Time) predicate.Host { + return predicate.Host(sql.FieldNotIn(FieldUpdatedTime, vs...)) +} + +// UpdatedTimeGT applies the GT predicate on the "updated_time" field. +func UpdatedTimeGT(v time.Time) predicate.Host { + return predicate.Host(sql.FieldGT(FieldUpdatedTime, v)) +} + +// UpdatedTimeGTE applies the GTE predicate on the "updated_time" field. +func UpdatedTimeGTE(v time.Time) predicate.Host { + return predicate.Host(sql.FieldGTE(FieldUpdatedTime, v)) +} + +// UpdatedTimeLT applies the LT predicate on the "updated_time" field. +func UpdatedTimeLT(v time.Time) predicate.Host { + return predicate.Host(sql.FieldLT(FieldUpdatedTime, v)) +} + +// UpdatedTimeLTE applies the LTE predicate on the "updated_time" field. +func UpdatedTimeLTE(v time.Time) predicate.Host { + return predicate.Host(sql.FieldLTE(FieldUpdatedTime, v)) +} + +// IPEQ applies the EQ predicate on the "ip" field. +func IPEQ(v ipconv.IP) predicate.Host { + vc := uint32(v) + return predicate.Host(sql.FieldEQ(FieldIP, vc)) +} + +// IPNEQ applies the NEQ predicate on the "ip" field. +func IPNEQ(v ipconv.IP) predicate.Host { + vc := uint32(v) + return predicate.Host(sql.FieldNEQ(FieldIP, vc)) +} + +// IPIn applies the In predicate on the "ip" field. +func IPIn(vs ...ipconv.IP) predicate.Host { + v := make([]any, len(vs)) + for i := range v { + v[i] = uint32(vs[i]) + } + return predicate.Host(sql.FieldIn(FieldIP, v...)) +} + +// IPNotIn applies the NotIn predicate on the "ip" field. +func IPNotIn(vs ...ipconv.IP) predicate.Host { + v := make([]any, len(vs)) + for i := range v { + v[i] = uint32(vs[i]) + } + return predicate.Host(sql.FieldNotIn(FieldIP, v...)) +} + +// IPGT applies the GT predicate on the "ip" field. +func IPGT(v ipconv.IP) predicate.Host { + vc := uint32(v) + return predicate.Host(sql.FieldGT(FieldIP, vc)) +} + +// IPGTE applies the GTE predicate on the "ip" field. +func IPGTE(v ipconv.IP) predicate.Host { + vc := uint32(v) + return predicate.Host(sql.FieldGTE(FieldIP, vc)) +} + +// IPLT applies the LT predicate on the "ip" field. +func IPLT(v ipconv.IP) predicate.Host { + vc := uint32(v) + return predicate.Host(sql.FieldLT(FieldIP, vc)) +} + +// IPLTE applies the LTE predicate on the "ip" field. +func IPLTE(v ipconv.IP) predicate.Host { + vc := uint32(v) + return predicate.Host(sql.FieldLTE(FieldIP, vc)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.Host) predicate.Host { + return predicate.Host(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.Host) predicate.Host { + return predicate.Host(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.Host) predicate.Host { + return predicate.Host(sql.NotPredicates(p)) +} diff --git a/westport/db/ent/host_create.go b/westport/db/ent/host_create.go new file mode 100644 index 0000000..e1c188f --- /dev/null +++ b/westport/db/ent/host_create.go @@ -0,0 +1,246 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/sprisa/west/util/ipconv" + "github.com/sprisa/west/westport/db/ent/host" +) + +// HostCreate is the builder for creating a Host entity. +type HostCreate struct { + config + mutation *HostMutation + hooks []Hook +} + +// SetCreatedTime sets the "created_time" field. +func (_c *HostCreate) SetCreatedTime(v time.Time) *HostCreate { + _c.mutation.SetCreatedTime(v) + return _c +} + +// SetNillableCreatedTime sets the "created_time" field if the given value is not nil. +func (_c *HostCreate) SetNillableCreatedTime(v *time.Time) *HostCreate { + if v != nil { + _c.SetCreatedTime(*v) + } + return _c +} + +// SetUpdatedTime sets the "updated_time" field. +func (_c *HostCreate) SetUpdatedTime(v time.Time) *HostCreate { + _c.mutation.SetUpdatedTime(v) + return _c +} + +// SetNillableUpdatedTime sets the "updated_time" field if the given value is not nil. +func (_c *HostCreate) SetNillableUpdatedTime(v *time.Time) *HostCreate { + if v != nil { + _c.SetUpdatedTime(*v) + } + return _c +} + +// SetIP sets the "ip" field. +func (_c *HostCreate) SetIP(v ipconv.IP) *HostCreate { + _c.mutation.SetIP(v) + return _c +} + +// Mutation returns the HostMutation object of the builder. +func (_c *HostCreate) Mutation() *HostMutation { + return _c.mutation +} + +// Save creates the Host in the database. +func (_c *HostCreate) Save(ctx context.Context) (*Host, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *HostCreate) SaveX(ctx context.Context) *Host { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *HostCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *HostCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *HostCreate) defaults() { + if _, ok := _c.mutation.CreatedTime(); !ok { + v := host.DefaultCreatedTime() + _c.mutation.SetCreatedTime(v) + } + if _, ok := _c.mutation.UpdatedTime(); !ok { + v := host.DefaultUpdatedTime() + _c.mutation.SetUpdatedTime(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *HostCreate) check() error { + if _, ok := _c.mutation.CreatedTime(); !ok { + return &ValidationError{Name: "created_time", err: errors.New(`ent: missing required field "Host.created_time"`)} + } + if _, ok := _c.mutation.UpdatedTime(); !ok { + return &ValidationError{Name: "updated_time", err: errors.New(`ent: missing required field "Host.updated_time"`)} + } + if _, ok := _c.mutation.IP(); !ok { + return &ValidationError{Name: "ip", err: errors.New(`ent: missing required field "Host.ip"`)} + } + if v, ok := _c.mutation.IP(); ok { + if err := host.IPValidator(uint32(v)); err != nil { + return &ValidationError{Name: "ip", err: fmt.Errorf(`ent: validator failed for field "Host.ip": %w`, err)} + } + } + return nil +} + +func (_c *HostCreate) sqlSave(ctx context.Context) (*Host, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + id := _spec.ID.Value.(int64) + _node.ID = int(id) + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *HostCreate) createSpec() (*Host, *sqlgraph.CreateSpec) { + var ( + _node = &Host{config: _c.config} + _spec = sqlgraph.NewCreateSpec(host.Table, sqlgraph.NewFieldSpec(host.FieldID, field.TypeInt)) + ) + if value, ok := _c.mutation.CreatedTime(); ok { + _spec.SetField(host.FieldCreatedTime, field.TypeTime, value) + _node.CreatedTime = value + } + if value, ok := _c.mutation.UpdatedTime(); ok { + _spec.SetField(host.FieldUpdatedTime, field.TypeTime, value) + _node.UpdatedTime = value + } + if value, ok := _c.mutation.IP(); ok { + _spec.SetField(host.FieldIP, field.TypeUint32, value) + _node.IP = value + } + return _node, _spec +} + +// HostCreateBulk is the builder for creating many Host entities in bulk. +type HostCreateBulk struct { + config + err error + builders []*HostCreate +} + +// Save creates the Host entities in the database. +func (_c *HostCreateBulk) Save(ctx context.Context) ([]*Host, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*Host, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*HostMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + if specs[i].ID.Value != nil { + id := specs[i].ID.Value.(int64) + nodes[i].ID = int(id) + } + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *HostCreateBulk) SaveX(ctx context.Context) []*Host { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *HostCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *HostCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/westport/db/ent/host_delete.go b/westport/db/ent/host_delete.go new file mode 100644 index 0000000..bb096fb --- /dev/null +++ b/westport/db/ent/host_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/sprisa/west/westport/db/ent/host" + "github.com/sprisa/west/westport/db/ent/predicate" +) + +// HostDelete is the builder for deleting a Host entity. +type HostDelete struct { + config + hooks []Hook + mutation *HostMutation +} + +// Where appends a list predicates to the HostDelete builder. +func (_d *HostDelete) Where(ps ...predicate.Host) *HostDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *HostDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *HostDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *HostDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(host.Table, sqlgraph.NewFieldSpec(host.FieldID, field.TypeInt)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// HostDeleteOne is the builder for deleting a single Host entity. +type HostDeleteOne struct { + _d *HostDelete +} + +// Where appends a list predicates to the HostDelete builder. +func (_d *HostDeleteOne) Where(ps ...predicate.Host) *HostDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *HostDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{host.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *HostDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/westport/db/ent/host_query.go b/westport/db/ent/host_query.go new file mode 100644 index 0000000..2d3dcb1 --- /dev/null +++ b/westport/db/ent/host_query.go @@ -0,0 +1,540 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/sprisa/west/westport/db/ent/host" + "github.com/sprisa/west/westport/db/ent/predicate" +) + +// HostQuery is the builder for querying Host entities. +type HostQuery struct { + config + ctx *QueryContext + order []host.OrderOption + inters []Interceptor + predicates []predicate.Host + modifiers []func(*sql.Selector) + loadTotal []func(context.Context, []*Host) error + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the HostQuery builder. +func (_q *HostQuery) Where(ps ...predicate.Host) *HostQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *HostQuery) Limit(limit int) *HostQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *HostQuery) Offset(offset int) *HostQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *HostQuery) Unique(unique bool) *HostQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *HostQuery) Order(o ...host.OrderOption) *HostQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first Host entity from the query. +// Returns a *NotFoundError when no Host was found. +func (_q *HostQuery) First(ctx context.Context) (*Host, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{host.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *HostQuery) FirstX(ctx context.Context) *Host { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first Host ID from the query. +// Returns a *NotFoundError when no Host ID was found. +func (_q *HostQuery) FirstID(ctx context.Context) (id int, err error) { + var ids []int + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{host.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *HostQuery) FirstIDX(ctx context.Context) int { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single Host entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one Host entity is found. +// Returns a *NotFoundError when no Host entities are found. +func (_q *HostQuery) Only(ctx context.Context) (*Host, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{host.Label} + default: + return nil, &NotSingularError{host.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *HostQuery) OnlyX(ctx context.Context) *Host { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only Host ID in the query. +// Returns a *NotSingularError when more than one Host ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *HostQuery) OnlyID(ctx context.Context) (id int, err error) { + var ids []int + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{host.Label} + default: + err = &NotSingularError{host.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *HostQuery) OnlyIDX(ctx context.Context) int { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of Hosts. +func (_q *HostQuery) All(ctx context.Context) ([]*Host, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*Host, *HostQuery]() + return withInterceptors[[]*Host](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *HostQuery) AllX(ctx context.Context) []*Host { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of Host IDs. +func (_q *HostQuery) IDs(ctx context.Context) (ids []int, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(host.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *HostQuery) IDsX(ctx context.Context) []int { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *HostQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*HostQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *HostQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *HostQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *HostQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the HostQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *HostQuery) Clone() *HostQuery { + if _q == nil { + return nil + } + return &HostQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]host.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.Host{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// CreatedTime time.Time `json:"created_time,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.Host.Query(). +// GroupBy(host.FieldCreatedTime). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *HostQuery) GroupBy(field string, fields ...string) *HostGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &HostGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = host.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// CreatedTime time.Time `json:"created_time,omitempty"` +// } +// +// client.Host.Query(). +// Select(host.FieldCreatedTime). +// Scan(ctx, &v) +func (_q *HostQuery) Select(fields ...string) *HostSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &HostSelect{HostQuery: _q} + sbuild.label = host.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a HostSelect configured with the given aggregations. +func (_q *HostQuery) Aggregate(fns ...AggregateFunc) *HostSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *HostQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !host.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *HostQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Host, error) { + var ( + nodes = []*Host{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*Host).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &Host{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + for i := range _q.loadTotal { + if err := _q.loadTotal[i](ctx, nodes); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (_q *HostQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *HostQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(host.Table, host.Columns, sqlgraph.NewFieldSpec(host.FieldID, field.TypeInt)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, host.FieldID) + for i := range fields { + if fields[i] != host.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *HostQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(host.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = host.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// HostGroupBy is the group-by builder for Host entities. +type HostGroupBy struct { + selector + build *HostQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *HostGroupBy) Aggregate(fns ...AggregateFunc) *HostGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *HostGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*HostQuery, *HostGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *HostGroupBy) sqlScan(ctx context.Context, root *HostQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// HostSelect is the builder for selecting fields of Host entities. +type HostSelect struct { + *HostQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *HostSelect) Aggregate(fns ...AggregateFunc) *HostSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *HostSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*HostQuery, *HostSelect](ctx, _s.HostQuery, _s, _s.inters, v) +} + +func (_s *HostSelect) sqlScan(ctx context.Context, root *HostQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/westport/db/ent/host_update.go b/westport/db/ent/host_update.go new file mode 100644 index 0000000..657567b --- /dev/null +++ b/westport/db/ent/host_update.go @@ -0,0 +1,212 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/sprisa/west/westport/db/ent/host" + "github.com/sprisa/west/westport/db/ent/predicate" +) + +// HostUpdate is the builder for updating Host entities. +type HostUpdate struct { + config + hooks []Hook + mutation *HostMutation +} + +// Where appends a list predicates to the HostUpdate builder. +func (_u *HostUpdate) Where(ps ...predicate.Host) *HostUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetUpdatedTime sets the "updated_time" field. +func (_u *HostUpdate) SetUpdatedTime(v time.Time) *HostUpdate { + _u.mutation.SetUpdatedTime(v) + return _u +} + +// Mutation returns the HostMutation object of the builder. +func (_u *HostUpdate) Mutation() *HostMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *HostUpdate) Save(ctx context.Context) (int, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *HostUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *HostUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *HostUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *HostUpdate) defaults() { + if _, ok := _u.mutation.UpdatedTime(); !ok { + v := host.UpdateDefaultUpdatedTime() + _u.mutation.SetUpdatedTime(v) + } +} + +func (_u *HostUpdate) sqlSave(ctx context.Context) (_node int, err error) { + _spec := sqlgraph.NewUpdateSpec(host.Table, host.Columns, sqlgraph.NewFieldSpec(host.FieldID, field.TypeInt)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.UpdatedTime(); ok { + _spec.SetField(host.FieldUpdatedTime, field.TypeTime, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{host.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// HostUpdateOne is the builder for updating a single Host entity. +type HostUpdateOne struct { + config + fields []string + hooks []Hook + mutation *HostMutation +} + +// SetUpdatedTime sets the "updated_time" field. +func (_u *HostUpdateOne) SetUpdatedTime(v time.Time) *HostUpdateOne { + _u.mutation.SetUpdatedTime(v) + return _u +} + +// Mutation returns the HostMutation object of the builder. +func (_u *HostUpdateOne) Mutation() *HostMutation { + return _u.mutation +} + +// Where appends a list predicates to the HostUpdate builder. +func (_u *HostUpdateOne) Where(ps ...predicate.Host) *HostUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *HostUpdateOne) Select(field string, fields ...string) *HostUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated Host entity. +func (_u *HostUpdateOne) Save(ctx context.Context) (*Host, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *HostUpdateOne) SaveX(ctx context.Context) *Host { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *HostUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *HostUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *HostUpdateOne) defaults() { + if _, ok := _u.mutation.UpdatedTime(); !ok { + v := host.UpdateDefaultUpdatedTime() + _u.mutation.SetUpdatedTime(v) + } +} + +func (_u *HostUpdateOne) sqlSave(ctx context.Context) (_node *Host, err error) { + _spec := sqlgraph.NewUpdateSpec(host.Table, host.Columns, sqlgraph.NewFieldSpec(host.FieldID, field.TypeInt)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Host.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, host.FieldID) + for _, f := range fields { + if !host.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != host.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.UpdatedTime(); ok { + _spec.SetField(host.FieldUpdatedTime, field.TypeTime, value) + } + _node = &Host{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{host.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/westport/db/ent/internal/schema.go b/westport/db/ent/internal/schema.go index 28b6fe0..46ada54 100644 --- a/westport/db/ent/internal/schema.go +++ b/westport/db/ent/internal/schema.go @@ -6,4 +6,4 @@ // Package internal holds a loadable version of the latest schema. package internal -const Schema = "{\"Schema\":\"github.com/sprisa/west/westport/db/schema\",\"Package\":\"github.com/sprisa/west/westport/db/ent\",\"Schemas\":[{\"name\":\"Device\",\"config\":{\"Table\":\"\"},\"fields\":[{\"name\":\"created_time\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"comment\":\"Time ent was created\"},{\"name\":\"updated_time\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"comment\":\"Time ent was updated\"},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Device name. Unique within the Network\"},{\"name\":\"ip\",\"type\":{\"Type\":16,\"Ident\":\"ipconv.IP\",\"PkgPath\":\"github.com/sprisa/west/util/ipconv\",\"PkgName\":\"ipconv\",\"Nillable\":false,\"RType\":{\"Name\":\"IP\",\"Ident\":\"ipconv.IP\",\"Kind\":10,\"PkgPath\":\"github.com/sprisa/west/util/ipconv\",\"Methods\":{\"ToIPV4\":{\"In\":[],\"Out\":[{\"Name\":\"IP\",\"Ident\":\"net.IP\",\"Kind\":23,\"PkgPath\":\"net\",\"Methods\":null}]},\"ToInt\":{\"In\":[],\"Out\":[{\"Name\":\"uint32\",\"Ident\":\"uint32\",\"Kind\":10,\"PkgPath\":\"\",\"Methods\":null}]},\"ToIpAddr\":{\"In\":[],\"Out\":[{\"Name\":\"Addr\",\"Ident\":\"netip.Addr\",\"Kind\":25,\"PkgPath\":\"net/netip\",\"Methods\":null}]}}}},\"immutable\":true,\"validators\":1,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Overlay IPv4 of host\"},{\"name\":\"leased_access_token\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true,\"comment\":\"Access Token leased to a provisioned device. Can only issue 1 at a time, similar to a lock. Used to verify only 1 instance of the Device is running.\"},{\"name\":\"token\",\"type\":{\"Type\":5,\"Ident\":\"helpers.EncryptedBytes\",\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"PkgName\":\"helpers\",\"Nillable\":true,\"RType\":{\"Name\":\"EncryptedBytes\",\"Ident\":\"helpers.EncryptedBytes\",\"Kind\":23,\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"Methods\":{\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true}],\"indexes\":[{\"unique\":true,\"fields\":[\"ip\"]},{\"unique\":true,\"fields\":[\"name\"]},{\"unique\":true,\"fields\":[\"token\"]}],\"annotations\":{\"EntGQL\":{}}},{\"name\":\"Settings\",\"config\":{\"Table\":\"\"},\"fields\":[{\"name\":\"created_time\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"comment\":\"Time ent was created\"},{\"name\":\"updated_time\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"comment\":\"Time ent was updated\"},{\"name\":\"domain_zone\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Domain zone to use for nameserver\"},{\"name\":\"cipher\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":\"aes\",\"default_kind\":24,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Nebula cipher. aes or chachapoly\"},{\"name\":\"ca_crt\",\"type\":{\"Type\":5,\"Ident\":\"helpers.EncryptedBytes\",\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"PkgName\":\"helpers\",\"Nillable\":true,\"RType\":{\"Name\":\"EncryptedBytes\",\"Ident\":\"helpers.EncryptedBytes\",\"Kind\":23,\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"Methods\":{\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"ca_key\",\"type\":{\"Type\":5,\"Ident\":\"helpers.EncryptedBytes\",\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"PkgName\":\"helpers\",\"Nillable\":true,\"RType\":{\"Name\":\"EncryptedBytes\",\"Ident\":\"helpers.EncryptedBytes\",\"Kind\":23,\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"Methods\":{\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true},{\"name\":\"lighthouse_crt\",\"type\":{\"Type\":5,\"Ident\":\"helpers.EncryptedBytes\",\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"PkgName\":\"helpers\",\"Nillable\":true,\"RType\":{\"Name\":\"EncryptedBytes\",\"Ident\":\"helpers.EncryptedBytes\",\"Kind\":23,\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"Methods\":{\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true},{\"name\":\"lighthouse_key\",\"type\":{\"Type\":5,\"Ident\":\"helpers.EncryptedBytes\",\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"PkgName\":\"helpers\",\"Nillable\":true,\"RType\":{\"Name\":\"EncryptedBytes\",\"Ident\":\"helpers.EncryptedBytes\",\"Kind\":23,\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"Methods\":{\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true},{\"name\":\"cidr\",\"type\":{\"Type\":7,\"Ident\":\"helpers.IpCidr\",\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"PkgName\":\"helpers\",\"Nillable\":false,\"RType\":{\"Name\":\"IpCidr\",\"Ident\":\"helpers.IpCidr\",\"Kind\":25,\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"Methods\":{\"Addr\":{\"In\":[],\"Out\":[{\"Name\":\"Addr\",\"Ident\":\"netip.Addr\",\"Kind\":25,\"PkgPath\":\"net/netip\",\"Methods\":null}]},\"AppendBinary\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"AppendText\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"AppendTo\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}]},\"Bits\":{\"In\":[],\"Out\":[{\"Name\":\"int\",\"Ident\":\"int\",\"Kind\":2,\"PkgPath\":\"\",\"Methods\":null}]},\"Contains\":{\"In\":[{\"Name\":\"Addr\",\"Ident\":\"netip.Addr\",\"Kind\":25,\"PkgPath\":\"net/netip\",\"Methods\":null}],\"Out\":[{\"Name\":\"bool\",\"Ident\":\"bool\",\"Kind\":1,\"PkgPath\":\"\",\"Methods\":null}]},\"IsSingleIP\":{\"In\":[],\"Out\":[{\"Name\":\"bool\",\"Ident\":\"bool\",\"Kind\":1,\"PkgPath\":\"\",\"Methods\":null}]},\"IsValid\":{\"In\":[],\"Out\":[{\"Name\":\"bool\",\"Ident\":\"bool\",\"Kind\":1,\"PkgPath\":\"\",\"Methods\":null}]},\"MarshalBinary\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"MarshalText\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Masked\":{\"In\":[],\"Out\":[{\"Name\":\"Prefix\",\"Ident\":\"netip.Prefix\",\"Kind\":25,\"PkgPath\":\"net/netip\",\"Methods\":null}]},\"Overlaps\":{\"In\":[{\"Name\":\"Prefix\",\"Ident\":\"netip.Prefix\",\"Kind\":25,\"PkgPath\":\"net/netip\",\"Methods\":null}],\"Out\":[{\"Name\":\"bool\",\"Ident\":\"bool\",\"Kind\":1,\"PkgPath\":\"\",\"Methods\":null}]},\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalBinary\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalText\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Network cidr range\"},{\"name\":\"port_overlay_ip\",\"type\":{\"Type\":16,\"Ident\":\"ipconv.IP\",\"PkgPath\":\"github.com/sprisa/west/util/ipconv\",\"PkgName\":\"ipconv\",\"Nillable\":false,\"RType\":{\"Name\":\"IP\",\"Ident\":\"ipconv.IP\",\"Kind\":10,\"PkgPath\":\"github.com/sprisa/west/util/ipconv\",\"Methods\":{\"ToIPV4\":{\"In\":[],\"Out\":[{\"Name\":\"IP\",\"Ident\":\"net.IP\",\"Kind\":23,\"PkgPath\":\"net\",\"Methods\":null}]},\"ToInt\":{\"In\":[],\"Out\":[{\"Name\":\"uint32\",\"Ident\":\"uint32\",\"Kind\":10,\"PkgPath\":\"\",\"Methods\":null}]},\"ToIpAddr\":{\"In\":[],\"Out\":[{\"Name\":\"Addr\",\"Ident\":\"netip.Addr\",\"Kind\":25,\"PkgPath\":\"net/netip\",\"Methods\":null}]}}}},\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Network cidr range\"},{\"name\":\"letsencrypt_registration\",\"type\":{\"Type\":5,\"Ident\":\"helpers.EncryptedBytes\",\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"PkgName\":\"helpers\",\"Nillable\":true,\"RType\":{\"Name\":\"EncryptedBytes\",\"Ident\":\"helpers.EncryptedBytes\",\"Kind\":23,\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"Methods\":{\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"optional\":true,\"position\":{\"Index\":8,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true},{\"name\":\"tls_cert\",\"type\":{\"Type\":5,\"Ident\":\"helpers.EncryptedBytes\",\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"PkgName\":\"helpers\",\"Nillable\":true,\"RType\":{\"Name\":\"EncryptedBytes\",\"Ident\":\"helpers.EncryptedBytes\",\"Kind\":23,\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"Methods\":{\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":9,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true},{\"name\":\"tls_cert_key\",\"type\":{\"Type\":5,\"Ident\":\"helpers.EncryptedBytes\",\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"PkgName\":\"helpers\",\"Nillable\":true,\"RType\":{\"Name\":\"EncryptedBytes\",\"Ident\":\"helpers.EncryptedBytes\",\"Kind\":23,\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"Methods\":{\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":10,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true}]}],\"Features\":[\"namedges\",\"privacy\",\"entql\",\"schema/snapshot\"]}" +const Schema = "{\"Schema\":\"github.com/sprisa/west/westport/db/schema\",\"Package\":\"github.com/sprisa/west/westport/db/ent\",\"Schemas\":[{\"name\":\"Device\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"host\",\"type\":\"Host\",\"unique\":true,\"required\":true}],\"fields\":[{\"name\":\"created_time\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"comment\":\"Time ent was created\"},{\"name\":\"updated_time\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"comment\":\"Time ent was updated\"},{\"name\":\"name\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Device name. Unique within the Network\"},{\"name\":\"ip\",\"type\":{\"Type\":16,\"Ident\":\"ipconv.IP\",\"PkgPath\":\"github.com/sprisa/west/util/ipconv\",\"PkgName\":\"ipconv\",\"Nillable\":false,\"RType\":{\"Name\":\"IP\",\"Ident\":\"ipconv.IP\",\"Kind\":10,\"PkgPath\":\"github.com/sprisa/west/util/ipconv\",\"Methods\":{\"ToIPV4\":{\"In\":[],\"Out\":[{\"Name\":\"IP\",\"Ident\":\"net.IP\",\"Kind\":23,\"PkgPath\":\"net\",\"Methods\":null}]},\"ToInt\":{\"In\":[],\"Out\":[{\"Name\":\"uint32\",\"Ident\":\"uint32\",\"Kind\":10,\"PkgPath\":\"\",\"Methods\":null}]},\"ToIpAddr\":{\"In\":[],\"Out\":[{\"Name\":\"Addr\",\"Ident\":\"netip.Addr\",\"Kind\":25,\"PkgPath\":\"net/netip\",\"Methods\":null}]}}}},\"immutable\":true,\"validators\":1,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"annotations\":{\"EntGQL\":{\"Directives\":[{\"arguments\":[{\"Comment\":null,\"Name\":\"forceResolver\",\"Value\":{\"Children\":null,\"Comment\":null,\"Definition\":null,\"ExpectedType\":null,\"ExpectedTypeHasDefault\":false,\"Kind\":5,\"Raw\":\"true\",\"VariableDefinition\":null}}],\"name\":\"goField\"}]}},\"comment\":\"Overlay IPv4 of host\"},{\"name\":\"leased_access_token\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true,\"comment\":\"Access Token leased to a provisioned device. Can only issue 1 at a time, similar to a lock. Used to verify only 1 instance of the Device is running.\"},{\"name\":\"token\",\"type\":{\"Type\":5,\"Ident\":\"helpers.EncryptedBytes\",\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"PkgName\":\"helpers\",\"Nillable\":true,\"RType\":{\"Name\":\"EncryptedBytes\",\"Ident\":\"helpers.EncryptedBytes\",\"Kind\":23,\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"Methods\":{\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true}],\"indexes\":[{\"unique\":true,\"fields\":[\"ip\"]},{\"unique\":true,\"fields\":[\"name\"]}],\"annotations\":{\"EntGQL\":{}}},{\"name\":\"Host\",\"config\":{\"Table\":\"\"},\"fields\":[{\"name\":\"created_time\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"comment\":\"Time ent was created\"},{\"name\":\"updated_time\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"comment\":\"Time ent was updated\"},{\"name\":\"ip\",\"type\":{\"Type\":16,\"Ident\":\"ipconv.IP\",\"PkgPath\":\"github.com/sprisa/west/util/ipconv\",\"PkgName\":\"ipconv\",\"Nillable\":false,\"RType\":{\"Name\":\"IP\",\"Ident\":\"ipconv.IP\",\"Kind\":10,\"PkgPath\":\"github.com/sprisa/west/util/ipconv\",\"Methods\":{\"ToIPV4\":{\"In\":[],\"Out\":[{\"Name\":\"IP\",\"Ident\":\"net.IP\",\"Kind\":23,\"PkgPath\":\"net\",\"Methods\":null}]},\"ToInt\":{\"In\":[],\"Out\":[{\"Name\":\"uint32\",\"Ident\":\"uint32\",\"Kind\":10,\"PkgPath\":\"\",\"Methods\":null}]},\"ToIpAddr\":{\"In\":[],\"Out\":[{\"Name\":\"Addr\",\"Ident\":\"netip.Addr\",\"Kind\":25,\"PkgPath\":\"net/netip\",\"Methods\":null}]}}}},\"immutable\":true,\"validators\":1,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}}],\"indexes\":[{\"unique\":true,\"fields\":[\"ip\"]}]},{\"name\":\"Lighthouse\",\"config\":{\"Table\":\"\"},\"edges\":[{\"name\":\"host\",\"type\":\"Host\",\"unique\":true,\"required\":true}],\"fields\":[{\"name\":\"created_time\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"comment\":\"Time ent was created\"},{\"name\":\"updated_time\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"comment\":\"Time ent was updated\"},{\"name\":\"ip\",\"type\":{\"Type\":16,\"Ident\":\"ipconv.IP\",\"PkgPath\":\"github.com/sprisa/west/util/ipconv\",\"PkgName\":\"ipconv\",\"Nillable\":false,\"RType\":{\"Name\":\"IP\",\"Ident\":\"ipconv.IP\",\"Kind\":10,\"PkgPath\":\"github.com/sprisa/west/util/ipconv\",\"Methods\":{\"ToIPV4\":{\"In\":[],\"Out\":[{\"Name\":\"IP\",\"Ident\":\"net.IP\",\"Kind\":23,\"PkgPath\":\"net\",\"Methods\":null}]},\"ToInt\":{\"In\":[],\"Out\":[{\"Name\":\"uint32\",\"Ident\":\"uint32\",\"Kind\":10,\"PkgPath\":\"\",\"Methods\":null}]},\"ToIpAddr\":{\"In\":[],\"Out\":[{\"Name\":\"Addr\",\"Ident\":\"netip.Addr\",\"Kind\":25,\"PkgPath\":\"net/netip\",\"Methods\":null}]}}}},\"immutable\":true,\"validators\":1,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Nebula overlay IPv4 of the lighthouse\"},{\"name\":\"endpoint\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Public Nebula host and UDP port\"},{\"name\":\"certificate\",\"type\":{\"Type\":5,\"Ident\":\"helpers.EncryptedBytes\",\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"PkgName\":\"helpers\",\"Nillable\":true,\"RType\":{\"Name\":\"EncryptedBytes\",\"Ident\":\"helpers.EncryptedBytes\",\"Kind\":23,\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"Methods\":{\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true},{\"name\":\"key\",\"type\":{\"Type\":5,\"Ident\":\"helpers.EncryptedBytes\",\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"PkgName\":\"helpers\",\"Nillable\":true,\"RType\":{\"Name\":\"EncryptedBytes\",\"Ident\":\"helpers.EncryptedBytes\",\"Kind\":23,\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"Methods\":{\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true},{\"name\":\"api_endpoint\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Physical API host:port for this node\"}],\"indexes\":[{\"unique\":true,\"fields\":[\"ip\"]}]},{\"name\":\"Settings\",\"config\":{\"Table\":\"\"},\"fields\":[{\"name\":\"created_time\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":true,\"MixinIndex\":0},\"comment\":\"Time ent was created\"},{\"name\":\"updated_time\",\"type\":{\"Type\":2,\"Ident\":\"\",\"PkgPath\":\"time\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_kind\":19,\"update_default\":true,\"position\":{\"Index\":1,\"MixedIn\":true,\"MixinIndex\":0},\"comment\":\"Time ent was updated\"},{\"name\":\"network_id\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":\"default\",\"default_kind\":24,\"immutable\":true,\"position\":{\"Index\":0,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"domain_zone\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"optional\":true,\"position\":{\"Index\":1,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Domain zone to use for nameserver\"},{\"name\":\"cipher\",\"type\":{\"Type\":7,\"Ident\":\"\",\"PkgPath\":\"\",\"PkgName\":\"\",\"Nillable\":false,\"RType\":null},\"default\":true,\"default_value\":\"aes\",\"default_kind\":24,\"position\":{\"Index\":2,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Nebula cipher. aes or chachapoly\"},{\"name\":\"ca_crt\",\"type\":{\"Type\":5,\"Ident\":\"helpers.EncryptedBytes\",\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"PkgName\":\"helpers\",\"Nillable\":true,\"RType\":{\"Name\":\"EncryptedBytes\",\"Ident\":\"helpers.EncryptedBytes\",\"Kind\":23,\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"Methods\":{\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"position\":{\"Index\":3,\"MixedIn\":false,\"MixinIndex\":0}},{\"name\":\"ca_key\",\"type\":{\"Type\":5,\"Ident\":\"helpers.EncryptedBytes\",\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"PkgName\":\"helpers\",\"Nillable\":true,\"RType\":{\"Name\":\"EncryptedBytes\",\"Ident\":\"helpers.EncryptedBytes\",\"Kind\":23,\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"Methods\":{\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"position\":{\"Index\":4,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true},{\"name\":\"cidr\",\"type\":{\"Type\":7,\"Ident\":\"helpers.IpCidr\",\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"PkgName\":\"helpers\",\"Nillable\":false,\"RType\":{\"Name\":\"IpCidr\",\"Ident\":\"helpers.IpCidr\",\"Kind\":25,\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"Methods\":{\"Addr\":{\"In\":[],\"Out\":[{\"Name\":\"Addr\",\"Ident\":\"netip.Addr\",\"Kind\":25,\"PkgPath\":\"net/netip\",\"Methods\":null}]},\"AppendBinary\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"AppendText\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"AppendTo\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}]},\"Bits\":{\"In\":[],\"Out\":[{\"Name\":\"int\",\"Ident\":\"int\",\"Kind\":2,\"PkgPath\":\"\",\"Methods\":null}]},\"Compare\":{\"In\":[{\"Name\":\"Prefix\",\"Ident\":\"netip.Prefix\",\"Kind\":25,\"PkgPath\":\"net/netip\",\"Methods\":null}],\"Out\":[{\"Name\":\"int\",\"Ident\":\"int\",\"Kind\":2,\"PkgPath\":\"\",\"Methods\":null}]},\"Contains\":{\"In\":[{\"Name\":\"Addr\",\"Ident\":\"netip.Addr\",\"Kind\":25,\"PkgPath\":\"net/netip\",\"Methods\":null}],\"Out\":[{\"Name\":\"bool\",\"Ident\":\"bool\",\"Kind\":1,\"PkgPath\":\"\",\"Methods\":null}]},\"IsSingleIP\":{\"In\":[],\"Out\":[{\"Name\":\"bool\",\"Ident\":\"bool\",\"Kind\":1,\"PkgPath\":\"\",\"Methods\":null}]},\"IsValid\":{\"In\":[],\"Out\":[{\"Name\":\"bool\",\"Ident\":\"bool\",\"Kind\":1,\"PkgPath\":\"\",\"Methods\":null}]},\"MarshalBinary\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"MarshalText\":{\"In\":[],\"Out\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Masked\":{\"In\":[],\"Out\":[{\"Name\":\"Prefix\",\"Ident\":\"netip.Prefix\",\"Kind\":25,\"PkgPath\":\"net/netip\",\"Methods\":null}]},\"Overlaps\":{\"In\":[{\"Name\":\"Prefix\",\"Ident\":\"netip.Prefix\",\"Kind\":25,\"PkgPath\":\"net/netip\",\"Methods\":null}],\"Out\":[{\"Name\":\"bool\",\"Ident\":\"bool\",\"Kind\":1,\"PkgPath\":\"\",\"Methods\":null}]},\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalBinary\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"UnmarshalText\":{\"In\":[{\"Name\":\"\",\"Ident\":\"[]uint8\",\"Kind\":23,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"position\":{\"Index\":5,\"MixedIn\":false,\"MixinIndex\":0},\"comment\":\"Network cidr range\"},{\"name\":\"letsencrypt_registration\",\"type\":{\"Type\":5,\"Ident\":\"helpers.EncryptedBytes\",\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"PkgName\":\"helpers\",\"Nillable\":true,\"RType\":{\"Name\":\"EncryptedBytes\",\"Ident\":\"helpers.EncryptedBytes\",\"Kind\":23,\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"Methods\":{\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"optional\":true,\"position\":{\"Index\":6,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true},{\"name\":\"tls_cert\",\"type\":{\"Type\":5,\"Ident\":\"helpers.EncryptedBytes\",\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"PkgName\":\"helpers\",\"Nillable\":true,\"RType\":{\"Name\":\"EncryptedBytes\",\"Ident\":\"helpers.EncryptedBytes\",\"Kind\":23,\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"Methods\":{\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":7,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true},{\"name\":\"tls_cert_key\",\"type\":{\"Type\":5,\"Ident\":\"helpers.EncryptedBytes\",\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"PkgName\":\"helpers\",\"Nillable\":true,\"RType\":{\"Name\":\"EncryptedBytes\",\"Ident\":\"helpers.EncryptedBytes\",\"Kind\":23,\"PkgPath\":\"github.com/sprisa/west/westport/db/helpers\",\"Methods\":{\"Scan\":{\"In\":[{\"Name\":\"\",\"Ident\":\"interface {}\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}],\"Out\":[{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]},\"String\":{\"In\":[],\"Out\":[{\"Name\":\"string\",\"Ident\":\"string\",\"Kind\":24,\"PkgPath\":\"\",\"Methods\":null}]},\"Value\":{\"In\":[],\"Out\":[{\"Name\":\"Value\",\"Ident\":\"driver.Value\",\"Kind\":20,\"PkgPath\":\"database/sql/driver\",\"Methods\":null},{\"Name\":\"error\",\"Ident\":\"error\",\"Kind\":20,\"PkgPath\":\"\",\"Methods\":null}]}}}},\"nillable\":true,\"optional\":true,\"position\":{\"Index\":8,\"MixedIn\":false,\"MixinIndex\":0},\"sensitive\":true}],\"indexes\":[{\"unique\":true,\"fields\":[\"network_id\"]}]}],\"Features\":[\"namedges\",\"privacy\",\"entql\",\"schema/snapshot\"]}" diff --git a/westport/db/ent/lighthouse.go b/westport/db/ent/lighthouse.go new file mode 100644 index 0000000..51c9411 --- /dev/null +++ b/westport/db/ent/lighthouse.go @@ -0,0 +1,213 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/sprisa/west/util/ipconv" + "github.com/sprisa/west/westport/db/ent/host" + "github.com/sprisa/west/westport/db/ent/lighthouse" + "github.com/sprisa/west/westport/db/helpers" +) + +// Lighthouse is the model entity for the Lighthouse schema. +type Lighthouse struct { + config `json:"-"` + // ID of the ent. + ID int `json:"id,omitempty"` + // Time ent was created + CreatedTime time.Time `json:"created_time,omitempty"` + // Time ent was updated + UpdatedTime time.Time `json:"updated_time,omitempty"` + // Nebula overlay IPv4 of the lighthouse + IP ipconv.IP `json:"ip,omitempty"` + // Public Nebula host and UDP port + Endpoint string `json:"endpoint,omitempty"` + // Certificate holds the value of the "certificate" field. + Certificate helpers.EncryptedBytes `json:"-"` + // Key holds the value of the "key" field. + Key helpers.EncryptedBytes `json:"-"` + // Physical API host:port for this node + APIEndpoint string `json:"api_endpoint,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the LighthouseQuery when eager-loading is set. + Edges LighthouseEdges `json:"edges"` + lighthouse_host *int + selectValues sql.SelectValues +} + +// LighthouseEdges holds the relations/edges for other nodes in the graph. +type LighthouseEdges struct { + // Host holds the value of the host edge. + Host *Host `json:"host,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [1]bool +} + +// HostOrErr returns the Host value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e LighthouseEdges) HostOrErr() (*Host, error) { + if e.Host != nil { + return e.Host, nil + } else if e.loadedTypes[0] { + return nil, &NotFoundError{label: host.Label} + } + return nil, &NotLoadedError{edge: "host"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*Lighthouse) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case lighthouse.FieldCertificate, lighthouse.FieldKey: + values[i] = new(helpers.EncryptedBytes) + case lighthouse.FieldID, lighthouse.FieldIP: + values[i] = new(sql.NullInt64) + case lighthouse.FieldEndpoint, lighthouse.FieldAPIEndpoint: + values[i] = new(sql.NullString) + case lighthouse.FieldCreatedTime, lighthouse.FieldUpdatedTime: + values[i] = new(sql.NullTime) + case lighthouse.ForeignKeys[0]: // lighthouse_host + values[i] = new(sql.NullInt64) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the Lighthouse fields. +func (_m *Lighthouse) assignValues(columns []string, values []any) error { + if m, n := len(values), len(columns); m < n { + return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) + } + for i := range columns { + switch columns[i] { + case lighthouse.FieldID: + value, ok := values[i].(*sql.NullInt64) + if !ok { + return fmt.Errorf("unexpected type %T for field id", value) + } + _m.ID = int(value.Int64) + case lighthouse.FieldCreatedTime: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_time", values[i]) + } else if value.Valid { + _m.CreatedTime = value.Time + } + case lighthouse.FieldUpdatedTime: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated_time", values[i]) + } else if value.Valid { + _m.UpdatedTime = value.Time + } + case lighthouse.FieldIP: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field ip", values[i]) + } else if value.Valid { + _m.IP = ipconv.IP(value.Int64) + } + case lighthouse.FieldEndpoint: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field endpoint", values[i]) + } else if value.Valid { + _m.Endpoint = value.String + } + case lighthouse.FieldCertificate: + if value, ok := values[i].(*helpers.EncryptedBytes); !ok { + return fmt.Errorf("unexpected type %T for field certificate", values[i]) + } else if value != nil { + _m.Certificate = *value + } + case lighthouse.FieldKey: + if value, ok := values[i].(*helpers.EncryptedBytes); !ok { + return fmt.Errorf("unexpected type %T for field key", values[i]) + } else if value != nil { + _m.Key = *value + } + case lighthouse.FieldAPIEndpoint: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field api_endpoint", values[i]) + } else if value.Valid { + _m.APIEndpoint = value.String + } + case lighthouse.ForeignKeys[0]: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for edge-field lighthouse_host", value) + } else if value.Valid { + _m.lighthouse_host = new(int) + *_m.lighthouse_host = int(value.Int64) + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the Lighthouse. +// This includes values selected through modifiers, order, etc. +func (_m *Lighthouse) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// QueryHost queries the "host" edge of the Lighthouse entity. +func (_m *Lighthouse) QueryHost() *HostQuery { + return NewLighthouseClient(_m.config).QueryHost(_m) +} + +// Update returns a builder for updating this Lighthouse. +// Note that you need to call Lighthouse.Unwrap() before calling this method if this Lighthouse +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *Lighthouse) Update() *LighthouseUpdateOne { + return NewLighthouseClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the Lighthouse entity that was returned from a transaction after it was closed, +// so that all future queries will be executed through the driver which created the transaction. +func (_m *Lighthouse) Unwrap() *Lighthouse { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: Lighthouse is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *Lighthouse) String() string { + var builder strings.Builder + builder.WriteString("Lighthouse(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("created_time=") + builder.WriteString(_m.CreatedTime.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated_time=") + builder.WriteString(_m.UpdatedTime.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("ip=") + builder.WriteString(fmt.Sprintf("%v", _m.IP)) + builder.WriteString(", ") + builder.WriteString("endpoint=") + builder.WriteString(_m.Endpoint) + builder.WriteString(", ") + builder.WriteString("certificate=") + builder.WriteString(", ") + builder.WriteString("key=") + builder.WriteString(", ") + builder.WriteString("api_endpoint=") + builder.WriteString(_m.APIEndpoint) + builder.WriteByte(')') + return builder.String() +} + +// Lighthouses is a parsable slice of Lighthouse. +type Lighthouses []*Lighthouse diff --git a/westport/db/ent/lighthouse/lighthouse.go b/westport/db/ent/lighthouse/lighthouse.go new file mode 100644 index 0000000..5b4fbd4 --- /dev/null +++ b/westport/db/ent/lighthouse/lighthouse.go @@ -0,0 +1,133 @@ +// Code generated by ent, DO NOT EDIT. + +package lighthouse + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" +) + +const ( + // Label holds the string label denoting the lighthouse type in the database. + Label = "lighthouse" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldCreatedTime holds the string denoting the created_time field in the database. + FieldCreatedTime = "created_time" + // FieldUpdatedTime holds the string denoting the updated_time field in the database. + FieldUpdatedTime = "updated_time" + // FieldIP holds the string denoting the ip field in the database. + FieldIP = "ip" + // FieldEndpoint holds the string denoting the endpoint field in the database. + FieldEndpoint = "endpoint" + // FieldCertificate holds the string denoting the certificate field in the database. + FieldCertificate = "certificate" + // FieldKey holds the string denoting the key field in the database. + FieldKey = "key" + // FieldAPIEndpoint holds the string denoting the api_endpoint field in the database. + FieldAPIEndpoint = "api_endpoint" + // EdgeHost holds the string denoting the host edge name in mutations. + EdgeHost = "host" + // Table holds the table name of the lighthouse in the database. + Table = "lighthouses" + // HostTable is the table that holds the host relation/edge. + HostTable = "lighthouses" + // HostInverseTable is the table name for the Host entity. + // It exists in this package in order to avoid circular dependency with the "host" package. + HostInverseTable = "hosts" + // HostColumn is the table column denoting the host relation/edge. + HostColumn = "lighthouse_host" +) + +// Columns holds all SQL columns for lighthouse fields. +var Columns = []string{ + FieldID, + FieldCreatedTime, + FieldUpdatedTime, + FieldIP, + FieldEndpoint, + FieldCertificate, + FieldKey, + FieldAPIEndpoint, +} + +// ForeignKeys holds the SQL foreign-keys that are owned by the "lighthouses" +// table and are not defined as standalone fields in the schema. +var ForeignKeys = []string{ + "lighthouse_host", +} + +// ValidColumn reports if the column name is valid (part of the table columns). +func ValidColumn(column string) bool { + for i := range Columns { + if column == Columns[i] { + return true + } + } + for i := range ForeignKeys { + if column == ForeignKeys[i] { + return true + } + } + return false +} + +var ( + // DefaultCreatedTime holds the default value on creation for the "created_time" field. + DefaultCreatedTime func() time.Time + // DefaultUpdatedTime holds the default value on creation for the "updated_time" field. + DefaultUpdatedTime func() time.Time + // UpdateDefaultUpdatedTime holds the default value on update for the "updated_time" field. + UpdateDefaultUpdatedTime func() time.Time + // IPValidator is a validator for the "ip" field. It is called by the builders before save. + IPValidator func(uint32) error +) + +// OrderOption defines the ordering options for the Lighthouse queries. +type OrderOption func(*sql.Selector) + +// ByID orders the results by the id field. +func ByID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldID, opts...).ToFunc() +} + +// ByCreatedTime orders the results by the created_time field. +func ByCreatedTime(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedTime, opts...).ToFunc() +} + +// ByUpdatedTime orders the results by the updated_time field. +func ByUpdatedTime(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedTime, opts...).ToFunc() +} + +// ByIP orders the results by the ip field. +func ByIP(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldIP, opts...).ToFunc() +} + +// ByEndpoint orders the results by the endpoint field. +func ByEndpoint(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldEndpoint, opts...).ToFunc() +} + +// ByAPIEndpoint orders the results by the api_endpoint field. +func ByAPIEndpoint(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldAPIEndpoint, opts...).ToFunc() +} + +// ByHostField orders the results by host field. +func ByHostField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newHostStep(), sql.OrderByField(field, opts...)) + } +} +func newHostStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(HostInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, HostTable, HostColumn), + ) +} diff --git a/westport/db/ent/lighthouse/where.go b/westport/db/ent/lighthouse/where.go new file mode 100644 index 0000000..befb1df --- /dev/null +++ b/westport/db/ent/lighthouse/where.go @@ -0,0 +1,476 @@ +// Code generated by ent, DO NOT EDIT. + +package lighthouse + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/sprisa/west/util/ipconv" + "github.com/sprisa/west/westport/db/ent/predicate" + "github.com/sprisa/west/westport/db/helpers" +) + +// ID filters vertices based on their ID field. +func ID(id int) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id int) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id int) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...int) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...int) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id int) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id int) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id int) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id int) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldLTE(FieldID, id)) +} + +// CreatedTime applies equality check predicate on the "created_time" field. It's identical to CreatedTimeEQ. +func CreatedTime(v time.Time) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldEQ(FieldCreatedTime, v)) +} + +// UpdatedTime applies equality check predicate on the "updated_time" field. It's identical to UpdatedTimeEQ. +func UpdatedTime(v time.Time) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldEQ(FieldUpdatedTime, v)) +} + +// IP applies equality check predicate on the "ip" field. It's identical to IPEQ. +func IP(v ipconv.IP) predicate.Lighthouse { + vc := uint32(v) + return predicate.Lighthouse(sql.FieldEQ(FieldIP, vc)) +} + +// Endpoint applies equality check predicate on the "endpoint" field. It's identical to EndpointEQ. +func Endpoint(v string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldEQ(FieldEndpoint, v)) +} + +// Certificate applies equality check predicate on the "certificate" field. It's identical to CertificateEQ. +func Certificate(v helpers.EncryptedBytes) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldEQ(FieldCertificate, v)) +} + +// Key applies equality check predicate on the "key" field. It's identical to KeyEQ. +func Key(v helpers.EncryptedBytes) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldEQ(FieldKey, v)) +} + +// APIEndpoint applies equality check predicate on the "api_endpoint" field. It's identical to APIEndpointEQ. +func APIEndpoint(v string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldEQ(FieldAPIEndpoint, v)) +} + +// CreatedTimeEQ applies the EQ predicate on the "created_time" field. +func CreatedTimeEQ(v time.Time) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldEQ(FieldCreatedTime, v)) +} + +// CreatedTimeNEQ applies the NEQ predicate on the "created_time" field. +func CreatedTimeNEQ(v time.Time) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldNEQ(FieldCreatedTime, v)) +} + +// CreatedTimeIn applies the In predicate on the "created_time" field. +func CreatedTimeIn(vs ...time.Time) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldIn(FieldCreatedTime, vs...)) +} + +// CreatedTimeNotIn applies the NotIn predicate on the "created_time" field. +func CreatedTimeNotIn(vs ...time.Time) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldNotIn(FieldCreatedTime, vs...)) +} + +// CreatedTimeGT applies the GT predicate on the "created_time" field. +func CreatedTimeGT(v time.Time) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldGT(FieldCreatedTime, v)) +} + +// CreatedTimeGTE applies the GTE predicate on the "created_time" field. +func CreatedTimeGTE(v time.Time) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldGTE(FieldCreatedTime, v)) +} + +// CreatedTimeLT applies the LT predicate on the "created_time" field. +func CreatedTimeLT(v time.Time) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldLT(FieldCreatedTime, v)) +} + +// CreatedTimeLTE applies the LTE predicate on the "created_time" field. +func CreatedTimeLTE(v time.Time) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldLTE(FieldCreatedTime, v)) +} + +// UpdatedTimeEQ applies the EQ predicate on the "updated_time" field. +func UpdatedTimeEQ(v time.Time) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldEQ(FieldUpdatedTime, v)) +} + +// UpdatedTimeNEQ applies the NEQ predicate on the "updated_time" field. +func UpdatedTimeNEQ(v time.Time) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldNEQ(FieldUpdatedTime, v)) +} + +// UpdatedTimeIn applies the In predicate on the "updated_time" field. +func UpdatedTimeIn(vs ...time.Time) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldIn(FieldUpdatedTime, vs...)) +} + +// UpdatedTimeNotIn applies the NotIn predicate on the "updated_time" field. +func UpdatedTimeNotIn(vs ...time.Time) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldNotIn(FieldUpdatedTime, vs...)) +} + +// UpdatedTimeGT applies the GT predicate on the "updated_time" field. +func UpdatedTimeGT(v time.Time) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldGT(FieldUpdatedTime, v)) +} + +// UpdatedTimeGTE applies the GTE predicate on the "updated_time" field. +func UpdatedTimeGTE(v time.Time) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldGTE(FieldUpdatedTime, v)) +} + +// UpdatedTimeLT applies the LT predicate on the "updated_time" field. +func UpdatedTimeLT(v time.Time) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldLT(FieldUpdatedTime, v)) +} + +// UpdatedTimeLTE applies the LTE predicate on the "updated_time" field. +func UpdatedTimeLTE(v time.Time) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldLTE(FieldUpdatedTime, v)) +} + +// IPEQ applies the EQ predicate on the "ip" field. +func IPEQ(v ipconv.IP) predicate.Lighthouse { + vc := uint32(v) + return predicate.Lighthouse(sql.FieldEQ(FieldIP, vc)) +} + +// IPNEQ applies the NEQ predicate on the "ip" field. +func IPNEQ(v ipconv.IP) predicate.Lighthouse { + vc := uint32(v) + return predicate.Lighthouse(sql.FieldNEQ(FieldIP, vc)) +} + +// IPIn applies the In predicate on the "ip" field. +func IPIn(vs ...ipconv.IP) predicate.Lighthouse { + v := make([]any, len(vs)) + for i := range v { + v[i] = uint32(vs[i]) + } + return predicate.Lighthouse(sql.FieldIn(FieldIP, v...)) +} + +// IPNotIn applies the NotIn predicate on the "ip" field. +func IPNotIn(vs ...ipconv.IP) predicate.Lighthouse { + v := make([]any, len(vs)) + for i := range v { + v[i] = uint32(vs[i]) + } + return predicate.Lighthouse(sql.FieldNotIn(FieldIP, v...)) +} + +// IPGT applies the GT predicate on the "ip" field. +func IPGT(v ipconv.IP) predicate.Lighthouse { + vc := uint32(v) + return predicate.Lighthouse(sql.FieldGT(FieldIP, vc)) +} + +// IPGTE applies the GTE predicate on the "ip" field. +func IPGTE(v ipconv.IP) predicate.Lighthouse { + vc := uint32(v) + return predicate.Lighthouse(sql.FieldGTE(FieldIP, vc)) +} + +// IPLT applies the LT predicate on the "ip" field. +func IPLT(v ipconv.IP) predicate.Lighthouse { + vc := uint32(v) + return predicate.Lighthouse(sql.FieldLT(FieldIP, vc)) +} + +// IPLTE applies the LTE predicate on the "ip" field. +func IPLTE(v ipconv.IP) predicate.Lighthouse { + vc := uint32(v) + return predicate.Lighthouse(sql.FieldLTE(FieldIP, vc)) +} + +// EndpointEQ applies the EQ predicate on the "endpoint" field. +func EndpointEQ(v string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldEQ(FieldEndpoint, v)) +} + +// EndpointNEQ applies the NEQ predicate on the "endpoint" field. +func EndpointNEQ(v string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldNEQ(FieldEndpoint, v)) +} + +// EndpointIn applies the In predicate on the "endpoint" field. +func EndpointIn(vs ...string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldIn(FieldEndpoint, vs...)) +} + +// EndpointNotIn applies the NotIn predicate on the "endpoint" field. +func EndpointNotIn(vs ...string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldNotIn(FieldEndpoint, vs...)) +} + +// EndpointGT applies the GT predicate on the "endpoint" field. +func EndpointGT(v string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldGT(FieldEndpoint, v)) +} + +// EndpointGTE applies the GTE predicate on the "endpoint" field. +func EndpointGTE(v string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldGTE(FieldEndpoint, v)) +} + +// EndpointLT applies the LT predicate on the "endpoint" field. +func EndpointLT(v string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldLT(FieldEndpoint, v)) +} + +// EndpointLTE applies the LTE predicate on the "endpoint" field. +func EndpointLTE(v string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldLTE(FieldEndpoint, v)) +} + +// EndpointContains applies the Contains predicate on the "endpoint" field. +func EndpointContains(v string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldContains(FieldEndpoint, v)) +} + +// EndpointHasPrefix applies the HasPrefix predicate on the "endpoint" field. +func EndpointHasPrefix(v string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldHasPrefix(FieldEndpoint, v)) +} + +// EndpointHasSuffix applies the HasSuffix predicate on the "endpoint" field. +func EndpointHasSuffix(v string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldHasSuffix(FieldEndpoint, v)) +} + +// EndpointEqualFold applies the EqualFold predicate on the "endpoint" field. +func EndpointEqualFold(v string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldEqualFold(FieldEndpoint, v)) +} + +// EndpointContainsFold applies the ContainsFold predicate on the "endpoint" field. +func EndpointContainsFold(v string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldContainsFold(FieldEndpoint, v)) +} + +// CertificateEQ applies the EQ predicate on the "certificate" field. +func CertificateEQ(v helpers.EncryptedBytes) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldEQ(FieldCertificate, v)) +} + +// CertificateNEQ applies the NEQ predicate on the "certificate" field. +func CertificateNEQ(v helpers.EncryptedBytes) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldNEQ(FieldCertificate, v)) +} + +// CertificateIn applies the In predicate on the "certificate" field. +func CertificateIn(vs ...helpers.EncryptedBytes) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldIn(FieldCertificate, vs...)) +} + +// CertificateNotIn applies the NotIn predicate on the "certificate" field. +func CertificateNotIn(vs ...helpers.EncryptedBytes) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldNotIn(FieldCertificate, vs...)) +} + +// CertificateGT applies the GT predicate on the "certificate" field. +func CertificateGT(v helpers.EncryptedBytes) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldGT(FieldCertificate, v)) +} + +// CertificateGTE applies the GTE predicate on the "certificate" field. +func CertificateGTE(v helpers.EncryptedBytes) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldGTE(FieldCertificate, v)) +} + +// CertificateLT applies the LT predicate on the "certificate" field. +func CertificateLT(v helpers.EncryptedBytes) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldLT(FieldCertificate, v)) +} + +// CertificateLTE applies the LTE predicate on the "certificate" field. +func CertificateLTE(v helpers.EncryptedBytes) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldLTE(FieldCertificate, v)) +} + +// KeyEQ applies the EQ predicate on the "key" field. +func KeyEQ(v helpers.EncryptedBytes) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldEQ(FieldKey, v)) +} + +// KeyNEQ applies the NEQ predicate on the "key" field. +func KeyNEQ(v helpers.EncryptedBytes) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldNEQ(FieldKey, v)) +} + +// KeyIn applies the In predicate on the "key" field. +func KeyIn(vs ...helpers.EncryptedBytes) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldIn(FieldKey, vs...)) +} + +// KeyNotIn applies the NotIn predicate on the "key" field. +func KeyNotIn(vs ...helpers.EncryptedBytes) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldNotIn(FieldKey, vs...)) +} + +// KeyGT applies the GT predicate on the "key" field. +func KeyGT(v helpers.EncryptedBytes) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldGT(FieldKey, v)) +} + +// KeyGTE applies the GTE predicate on the "key" field. +func KeyGTE(v helpers.EncryptedBytes) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldGTE(FieldKey, v)) +} + +// KeyLT applies the LT predicate on the "key" field. +func KeyLT(v helpers.EncryptedBytes) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldLT(FieldKey, v)) +} + +// KeyLTE applies the LTE predicate on the "key" field. +func KeyLTE(v helpers.EncryptedBytes) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldLTE(FieldKey, v)) +} + +// APIEndpointEQ applies the EQ predicate on the "api_endpoint" field. +func APIEndpointEQ(v string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldEQ(FieldAPIEndpoint, v)) +} + +// APIEndpointNEQ applies the NEQ predicate on the "api_endpoint" field. +func APIEndpointNEQ(v string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldNEQ(FieldAPIEndpoint, v)) +} + +// APIEndpointIn applies the In predicate on the "api_endpoint" field. +func APIEndpointIn(vs ...string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldIn(FieldAPIEndpoint, vs...)) +} + +// APIEndpointNotIn applies the NotIn predicate on the "api_endpoint" field. +func APIEndpointNotIn(vs ...string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldNotIn(FieldAPIEndpoint, vs...)) +} + +// APIEndpointGT applies the GT predicate on the "api_endpoint" field. +func APIEndpointGT(v string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldGT(FieldAPIEndpoint, v)) +} + +// APIEndpointGTE applies the GTE predicate on the "api_endpoint" field. +func APIEndpointGTE(v string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldGTE(FieldAPIEndpoint, v)) +} + +// APIEndpointLT applies the LT predicate on the "api_endpoint" field. +func APIEndpointLT(v string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldLT(FieldAPIEndpoint, v)) +} + +// APIEndpointLTE applies the LTE predicate on the "api_endpoint" field. +func APIEndpointLTE(v string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldLTE(FieldAPIEndpoint, v)) +} + +// APIEndpointContains applies the Contains predicate on the "api_endpoint" field. +func APIEndpointContains(v string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldContains(FieldAPIEndpoint, v)) +} + +// APIEndpointHasPrefix applies the HasPrefix predicate on the "api_endpoint" field. +func APIEndpointHasPrefix(v string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldHasPrefix(FieldAPIEndpoint, v)) +} + +// APIEndpointHasSuffix applies the HasSuffix predicate on the "api_endpoint" field. +func APIEndpointHasSuffix(v string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldHasSuffix(FieldAPIEndpoint, v)) +} + +// APIEndpointEqualFold applies the EqualFold predicate on the "api_endpoint" field. +func APIEndpointEqualFold(v string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldEqualFold(FieldAPIEndpoint, v)) +} + +// APIEndpointContainsFold applies the ContainsFold predicate on the "api_endpoint" field. +func APIEndpointContainsFold(v string) predicate.Lighthouse { + return predicate.Lighthouse(sql.FieldContainsFold(FieldAPIEndpoint, v)) +} + +// HasHost applies the HasEdge predicate on the "host" edge. +func HasHost() predicate.Lighthouse { + return predicate.Lighthouse(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, HostTable, HostColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasHostWith applies the HasEdge predicate on the "host" edge with a given conditions (other predicates). +func HasHostWith(preds ...predicate.Host) predicate.Lighthouse { + return predicate.Lighthouse(func(s *sql.Selector) { + step := newHostStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.Lighthouse) predicate.Lighthouse { + return predicate.Lighthouse(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.Lighthouse) predicate.Lighthouse { + return predicate.Lighthouse(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.Lighthouse) predicate.Lighthouse { + return predicate.Lighthouse(sql.NotPredicates(p)) +} diff --git a/westport/db/ent/lighthouse_create.go b/westport/db/ent/lighthouse_create.go new file mode 100644 index 0000000..6c39015 --- /dev/null +++ b/westport/db/ent/lighthouse_create.go @@ -0,0 +1,331 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/sprisa/west/util/ipconv" + "github.com/sprisa/west/westport/db/ent/host" + "github.com/sprisa/west/westport/db/ent/lighthouse" + "github.com/sprisa/west/westport/db/helpers" +) + +// LighthouseCreate is the builder for creating a Lighthouse entity. +type LighthouseCreate struct { + config + mutation *LighthouseMutation + hooks []Hook +} + +// SetCreatedTime sets the "created_time" field. +func (_c *LighthouseCreate) SetCreatedTime(v time.Time) *LighthouseCreate { + _c.mutation.SetCreatedTime(v) + return _c +} + +// SetNillableCreatedTime sets the "created_time" field if the given value is not nil. +func (_c *LighthouseCreate) SetNillableCreatedTime(v *time.Time) *LighthouseCreate { + if v != nil { + _c.SetCreatedTime(*v) + } + return _c +} + +// SetUpdatedTime sets the "updated_time" field. +func (_c *LighthouseCreate) SetUpdatedTime(v time.Time) *LighthouseCreate { + _c.mutation.SetUpdatedTime(v) + return _c +} + +// SetNillableUpdatedTime sets the "updated_time" field if the given value is not nil. +func (_c *LighthouseCreate) SetNillableUpdatedTime(v *time.Time) *LighthouseCreate { + if v != nil { + _c.SetUpdatedTime(*v) + } + return _c +} + +// SetIP sets the "ip" field. +func (_c *LighthouseCreate) SetIP(v ipconv.IP) *LighthouseCreate { + _c.mutation.SetIP(v) + return _c +} + +// SetEndpoint sets the "endpoint" field. +func (_c *LighthouseCreate) SetEndpoint(v string) *LighthouseCreate { + _c.mutation.SetEndpoint(v) + return _c +} + +// SetCertificate sets the "certificate" field. +func (_c *LighthouseCreate) SetCertificate(v helpers.EncryptedBytes) *LighthouseCreate { + _c.mutation.SetCertificate(v) + return _c +} + +// SetKey sets the "key" field. +func (_c *LighthouseCreate) SetKey(v helpers.EncryptedBytes) *LighthouseCreate { + _c.mutation.SetKey(v) + return _c +} + +// SetAPIEndpoint sets the "api_endpoint" field. +func (_c *LighthouseCreate) SetAPIEndpoint(v string) *LighthouseCreate { + _c.mutation.SetAPIEndpoint(v) + return _c +} + +// SetHostID sets the "host" edge to the Host entity by ID. +func (_c *LighthouseCreate) SetHostID(id int) *LighthouseCreate { + _c.mutation.SetHostID(id) + return _c +} + +// SetHost sets the "host" edge to the Host entity. +func (_c *LighthouseCreate) SetHost(v *Host) *LighthouseCreate { + return _c.SetHostID(v.ID) +} + +// Mutation returns the LighthouseMutation object of the builder. +func (_c *LighthouseCreate) Mutation() *LighthouseMutation { + return _c.mutation +} + +// Save creates the Lighthouse in the database. +func (_c *LighthouseCreate) Save(ctx context.Context) (*Lighthouse, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *LighthouseCreate) SaveX(ctx context.Context) *Lighthouse { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *LighthouseCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *LighthouseCreate) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_c *LighthouseCreate) defaults() { + if _, ok := _c.mutation.CreatedTime(); !ok { + v := lighthouse.DefaultCreatedTime() + _c.mutation.SetCreatedTime(v) + } + if _, ok := _c.mutation.UpdatedTime(); !ok { + v := lighthouse.DefaultUpdatedTime() + _c.mutation.SetUpdatedTime(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *LighthouseCreate) check() error { + if _, ok := _c.mutation.CreatedTime(); !ok { + return &ValidationError{Name: "created_time", err: errors.New(`ent: missing required field "Lighthouse.created_time"`)} + } + if _, ok := _c.mutation.UpdatedTime(); !ok { + return &ValidationError{Name: "updated_time", err: errors.New(`ent: missing required field "Lighthouse.updated_time"`)} + } + if _, ok := _c.mutation.IP(); !ok { + return &ValidationError{Name: "ip", err: errors.New(`ent: missing required field "Lighthouse.ip"`)} + } + if v, ok := _c.mutation.IP(); ok { + if err := lighthouse.IPValidator(uint32(v)); err != nil { + return &ValidationError{Name: "ip", err: fmt.Errorf(`ent: validator failed for field "Lighthouse.ip": %w`, err)} + } + } + if _, ok := _c.mutation.Endpoint(); !ok { + return &ValidationError{Name: "endpoint", err: errors.New(`ent: missing required field "Lighthouse.endpoint"`)} + } + if _, ok := _c.mutation.Certificate(); !ok { + return &ValidationError{Name: "certificate", err: errors.New(`ent: missing required field "Lighthouse.certificate"`)} + } + if _, ok := _c.mutation.Key(); !ok { + return &ValidationError{Name: "key", err: errors.New(`ent: missing required field "Lighthouse.key"`)} + } + if _, ok := _c.mutation.APIEndpoint(); !ok { + return &ValidationError{Name: "api_endpoint", err: errors.New(`ent: missing required field "Lighthouse.api_endpoint"`)} + } + if len(_c.mutation.HostIDs()) == 0 { + return &ValidationError{Name: "host", err: errors.New(`ent: missing required edge "Lighthouse.host"`)} + } + return nil +} + +func (_c *LighthouseCreate) sqlSave(ctx context.Context) (*Lighthouse, error) { + if err := _c.check(); err != nil { + return nil, err + } + _node, _spec := _c.createSpec() + if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + id := _spec.ID.Value.(int64) + _node.ID = int(id) + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *LighthouseCreate) createSpec() (*Lighthouse, *sqlgraph.CreateSpec) { + var ( + _node = &Lighthouse{config: _c.config} + _spec = sqlgraph.NewCreateSpec(lighthouse.Table, sqlgraph.NewFieldSpec(lighthouse.FieldID, field.TypeInt)) + ) + if value, ok := _c.mutation.CreatedTime(); ok { + _spec.SetField(lighthouse.FieldCreatedTime, field.TypeTime, value) + _node.CreatedTime = value + } + if value, ok := _c.mutation.UpdatedTime(); ok { + _spec.SetField(lighthouse.FieldUpdatedTime, field.TypeTime, value) + _node.UpdatedTime = value + } + if value, ok := _c.mutation.IP(); ok { + _spec.SetField(lighthouse.FieldIP, field.TypeUint32, value) + _node.IP = value + } + if value, ok := _c.mutation.Endpoint(); ok { + _spec.SetField(lighthouse.FieldEndpoint, field.TypeString, value) + _node.Endpoint = value + } + if value, ok := _c.mutation.Certificate(); ok { + _spec.SetField(lighthouse.FieldCertificate, field.TypeBytes, value) + _node.Certificate = value + } + if value, ok := _c.mutation.Key(); ok { + _spec.SetField(lighthouse.FieldKey, field.TypeBytes, value) + _node.Key = value + } + if value, ok := _c.mutation.APIEndpoint(); ok { + _spec.SetField(lighthouse.FieldAPIEndpoint, field.TypeString, value) + _node.APIEndpoint = value + } + if nodes := _c.mutation.HostIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: lighthouse.HostTable, + Columns: []string{lighthouse.HostColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(host.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.lighthouse_host = &nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// LighthouseCreateBulk is the builder for creating many Lighthouse entities in bulk. +type LighthouseCreateBulk struct { + config + err error + builders []*LighthouseCreate +} + +// Save creates the Lighthouse entities in the database. +func (_c *LighthouseCreateBulk) Save(ctx context.Context) ([]*Lighthouse, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*Lighthouse, len(_c.builders)) + mutators := make([]Mutator, len(_c.builders)) + for i := range _c.builders { + func(i int, root context.Context) { + builder := _c.builders[i] + builder.defaults() + var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { + mutation, ok := m.(*LighthouseMutation) + if !ok { + return nil, fmt.Errorf("unexpected mutation type %T", m) + } + if err := builder.check(); err != nil { + return nil, err + } + builder.mutation = mutation + var err error + nodes[i], specs[i] = builder.createSpec() + if i < len(mutators)-1 { + _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation) + } else { + spec := &sqlgraph.BatchCreateSpec{Nodes: specs} + // Invoke the actual operation on the latest mutation in the chain. + if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil { + if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + } + } + if err != nil { + return nil, err + } + mutation.id = &nodes[i].ID + if specs[i].ID.Value != nil { + id := specs[i].ID.Value.(int64) + nodes[i].ID = int(id) + } + mutation.done = true + return nodes[i], nil + }) + for i := len(builder.hooks) - 1; i >= 0; i-- { + mut = builder.hooks[i](mut) + } + mutators[i] = mut + }(i, ctx) + } + if len(mutators) > 0 { + if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil { + return nil, err + } + } + return nodes, nil +} + +// SaveX is like Save, but panics if an error occurs. +func (_c *LighthouseCreateBulk) SaveX(ctx context.Context) []*Lighthouse { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *LighthouseCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *LighthouseCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/westport/db/ent/lighthouse_delete.go b/westport/db/ent/lighthouse_delete.go new file mode 100644 index 0000000..cccbe25 --- /dev/null +++ b/westport/db/ent/lighthouse_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/sprisa/west/westport/db/ent/lighthouse" + "github.com/sprisa/west/westport/db/ent/predicate" +) + +// LighthouseDelete is the builder for deleting a Lighthouse entity. +type LighthouseDelete struct { + config + hooks []Hook + mutation *LighthouseMutation +} + +// Where appends a list predicates to the LighthouseDelete builder. +func (_d *LighthouseDelete) Where(ps ...predicate.Lighthouse) *LighthouseDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *LighthouseDelete) Exec(ctx context.Context) (int, error) { + return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks) +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *LighthouseDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *LighthouseDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(lighthouse.Table, sqlgraph.NewFieldSpec(lighthouse.FieldID, field.TypeInt)) + if ps := _d.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec) + if err != nil && sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + _d.mutation.done = true + return affected, err +} + +// LighthouseDeleteOne is the builder for deleting a single Lighthouse entity. +type LighthouseDeleteOne struct { + _d *LighthouseDelete +} + +// Where appends a list predicates to the LighthouseDelete builder. +func (_d *LighthouseDeleteOne) Where(ps ...predicate.Lighthouse) *LighthouseDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *LighthouseDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{lighthouse.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *LighthouseDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/westport/db/ent/lighthouse_query.go b/westport/db/ent/lighthouse_query.go new file mode 100644 index 0000000..01b90d3 --- /dev/null +++ b/westport/db/ent/lighthouse_query.go @@ -0,0 +1,627 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/sprisa/west/westport/db/ent/host" + "github.com/sprisa/west/westport/db/ent/lighthouse" + "github.com/sprisa/west/westport/db/ent/predicate" +) + +// LighthouseQuery is the builder for querying Lighthouse entities. +type LighthouseQuery struct { + config + ctx *QueryContext + order []lighthouse.OrderOption + inters []Interceptor + predicates []predicate.Lighthouse + withHost *HostQuery + withFKs bool + modifiers []func(*sql.Selector) + loadTotal []func(context.Context, []*Lighthouse) error + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the LighthouseQuery builder. +func (_q *LighthouseQuery) Where(ps ...predicate.Lighthouse) *LighthouseQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *LighthouseQuery) Limit(limit int) *LighthouseQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *LighthouseQuery) Offset(offset int) *LighthouseQuery { + _q.ctx.Offset = &offset + return _q +} + +// Unique configures the query builder to filter duplicate records on query. +// By default, unique is set to true, and can be disabled using this method. +func (_q *LighthouseQuery) Unique(unique bool) *LighthouseQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *LighthouseQuery) Order(o ...lighthouse.OrderOption) *LighthouseQuery { + _q.order = append(_q.order, o...) + return _q +} + +// QueryHost chains the current query on the "host" edge. +func (_q *LighthouseQuery) QueryHost() *HostQuery { + query := (&HostClient{config: _q.config}).Query() + query.path = func(ctx context.Context) (fromU *sql.Selector, err error) { + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + selector := _q.sqlQuery(ctx) + if err := selector.Err(); err != nil { + return nil, err + } + step := sqlgraph.NewStep( + sqlgraph.From(lighthouse.Table, lighthouse.FieldID, selector), + sqlgraph.To(host.Table, host.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, lighthouse.HostTable, lighthouse.HostColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first Lighthouse entity from the query. +// Returns a *NotFoundError when no Lighthouse was found. +func (_q *LighthouseQuery) First(ctx context.Context) (*Lighthouse, error) { + nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst)) + if err != nil { + return nil, err + } + if len(nodes) == 0 { + return nil, &NotFoundError{lighthouse.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *LighthouseQuery) FirstX(ctx context.Context) *Lighthouse { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first Lighthouse ID from the query. +// Returns a *NotFoundError when no Lighthouse ID was found. +func (_q *LighthouseQuery) FirstID(ctx context.Context) (id int, err error) { + var ids []int + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{lighthouse.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *LighthouseQuery) FirstIDX(ctx context.Context) int { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single Lighthouse entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one Lighthouse entity is found. +// Returns a *NotFoundError when no Lighthouse entities are found. +func (_q *LighthouseQuery) Only(ctx context.Context) (*Lighthouse, error) { + nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly)) + if err != nil { + return nil, err + } + switch len(nodes) { + case 1: + return nodes[0], nil + case 0: + return nil, &NotFoundError{lighthouse.Label} + default: + return nil, &NotSingularError{lighthouse.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *LighthouseQuery) OnlyX(ctx context.Context) *Lighthouse { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only Lighthouse ID in the query. +// Returns a *NotSingularError when more than one Lighthouse ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *LighthouseQuery) OnlyID(ctx context.Context) (id int, err error) { + var ids []int + if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil { + return + } + switch len(ids) { + case 1: + id = ids[0] + case 0: + err = &NotFoundError{lighthouse.Label} + default: + err = &NotSingularError{lighthouse.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *LighthouseQuery) OnlyIDX(ctx context.Context) int { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of Lighthouses. +func (_q *LighthouseQuery) All(ctx context.Context) ([]*Lighthouse, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*Lighthouse, *LighthouseQuery]() + return withInterceptors[[]*Lighthouse](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *LighthouseQuery) AllX(ctx context.Context) []*Lighthouse { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of Lighthouse IDs. +func (_q *LighthouseQuery) IDs(ctx context.Context) (ids []int, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(lighthouse.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *LighthouseQuery) IDsX(ctx context.Context) []int { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *LighthouseQuery) Count(ctx context.Context) (int, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount) + if err := _q.prepareQuery(ctx); err != nil { + return 0, err + } + return withInterceptors[int](ctx, _q, querierCount[*LighthouseQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *LighthouseQuery) CountX(ctx context.Context) int { + count, err := _q.Count(ctx) + if err != nil { + panic(err) + } + return count +} + +// Exist returns true if the query has elements in the graph. +func (_q *LighthouseQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.FirstID(ctx); { + case IsNotFound(err): + return false, nil + case err != nil: + return false, fmt.Errorf("ent: check existence: %w", err) + default: + return true, nil + } +} + +// ExistX is like Exist, but panics if an error occurs. +func (_q *LighthouseQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the LighthouseQuery builder, including all associated steps. It can be +// used to prepare common query builders and use them differently after the clone is made. +func (_q *LighthouseQuery) Clone() *LighthouseQuery { + if _q == nil { + return nil + } + return &LighthouseQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]lighthouse.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.Lighthouse{}, _q.predicates...), + withHost: _q.withHost.Clone(), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// WithHost tells the query-builder to eager-load the nodes that are connected to +// the "host" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *LighthouseQuery) WithHost(opts ...func(*HostQuery)) *LighthouseQuery { + query := (&HostClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withHost = query + return _q +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// CreatedTime time.Time `json:"created_time,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.Lighthouse.Query(). +// GroupBy(lighthouse.FieldCreatedTime). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *LighthouseQuery) GroupBy(field string, fields ...string) *LighthouseGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &LighthouseGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = lighthouse.Label + grbuild.scan = grbuild.Scan + return grbuild +} + +// Select allows the selection one or more fields/columns for the given query, +// instead of selecting all fields in the entity. +// +// Example: +// +// var v []struct { +// CreatedTime time.Time `json:"created_time,omitempty"` +// } +// +// client.Lighthouse.Query(). +// Select(lighthouse.FieldCreatedTime). +// Scan(ctx, &v) +func (_q *LighthouseQuery) Select(fields ...string) *LighthouseSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &LighthouseSelect{LighthouseQuery: _q} + sbuild.label = lighthouse.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a LighthouseSelect configured with the given aggregations. +func (_q *LighthouseQuery) Aggregate(fns ...AggregateFunc) *LighthouseSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *LighthouseQuery) prepareQuery(ctx context.Context) error { + for _, inter := range _q.inters { + if inter == nil { + return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)") + } + if trv, ok := inter.(Traverser); ok { + if err := trv.Traverse(ctx, _q); err != nil { + return err + } + } + } + for _, f := range _q.ctx.Fields { + if !lighthouse.ValidColumn(f) { + return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + } + if _q.path != nil { + prev, err := _q.path(ctx) + if err != nil { + return err + } + _q.sql = prev + } + return nil +} + +func (_q *LighthouseQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Lighthouse, error) { + var ( + nodes = []*Lighthouse{} + withFKs = _q.withFKs + _spec = _q.querySpec() + loadedTypes = [1]bool{ + _q.withHost != nil, + } + ) + if _q.withHost != nil { + withFKs = true + } + if withFKs { + _spec.Node.Columns = append(_spec.Node.Columns, lighthouse.ForeignKeys...) + } + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*Lighthouse).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &Lighthouse{config: _q.config} + nodes = append(nodes, node) + node.Edges.loadedTypes = loadedTypes + return node.assignValues(columns, values) + } + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + if query := _q.withHost; query != nil { + if err := _q.loadHost(ctx, query, nodes, nil, + func(n *Lighthouse, e *Host) { n.Edges.Host = e }); err != nil { + return nil, err + } + } + for i := range _q.loadTotal { + if err := _q.loadTotal[i](ctx, nodes); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (_q *LighthouseQuery) loadHost(ctx context.Context, query *HostQuery, nodes []*Lighthouse, init func(*Lighthouse), assign func(*Lighthouse, *Host)) error { + ids := make([]int, 0, len(nodes)) + nodeids := make(map[int][]*Lighthouse) + for i := range nodes { + if nodes[i].lighthouse_host == nil { + continue + } + fk := *nodes[i].lighthouse_host + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(host.IDIn(ids...)) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nodeids[n.ID] + if !ok { + return fmt.Errorf(`unexpected foreign-key "lighthouse_host" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} + +func (_q *LighthouseQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + if len(_q.modifiers) > 0 { + _spec.Modifiers = _q.modifiers + } + _spec.Node.Columns = _q.ctx.Fields + if len(_q.ctx.Fields) > 0 { + _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique + } + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *LighthouseQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(lighthouse.Table, lighthouse.Columns, sqlgraph.NewFieldSpec(lighthouse.FieldID, field.TypeInt)) + _spec.From = _q.sql + if unique := _q.ctx.Unique; unique != nil { + _spec.Unique = *unique + } else if _q.path != nil { + _spec.Unique = true + } + if fields := _q.ctx.Fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, lighthouse.FieldID) + for i := range fields { + if fields[i] != lighthouse.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + } + if ps := _q.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if limit := _q.ctx.Limit; limit != nil { + _spec.Limit = *limit + } + if offset := _q.ctx.Offset; offset != nil { + _spec.Offset = *offset + } + if ps := _q.order; len(ps) > 0 { + _spec.Order = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + return _spec +} + +func (_q *LighthouseQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(lighthouse.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = lighthouse.Columns + } + selector := builder.Select(t1.Columns(columns...)...).From(t1) + if _q.sql != nil { + selector = _q.sql + selector.Select(selector.Columns(columns...)...) + } + if _q.ctx.Unique != nil && *_q.ctx.Unique { + selector.Distinct() + } + for _, p := range _q.predicates { + p(selector) + } + for _, p := range _q.order { + p(selector) + } + if offset := _q.ctx.Offset; offset != nil { + // limit is mandatory for offset clause. We start + // with default value, and override it below if needed. + selector.Offset(*offset).Limit(math.MaxInt32) + } + if limit := _q.ctx.Limit; limit != nil { + selector.Limit(*limit) + } + return selector +} + +// LighthouseGroupBy is the group-by builder for Lighthouse entities. +type LighthouseGroupBy struct { + selector + build *LighthouseQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *LighthouseGroupBy) Aggregate(fns ...AggregateFunc) *LighthouseGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *LighthouseGroupBy) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy) + if err := _g.build.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*LighthouseQuery, *LighthouseGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *LighthouseGroupBy) sqlScan(ctx context.Context, root *LighthouseQuery, v any) error { + selector := root.sqlQuery(ctx).Select() + aggregation := make([]string, 0, len(_g.fns)) + for _, fn := range _g.fns { + aggregation = append(aggregation, fn(selector)) + } + if len(selector.SelectedColumns()) == 0 { + columns := make([]string, 0, len(*_g.flds)+len(_g.fns)) + for _, f := range *_g.flds { + columns = append(columns, selector.C(f)) + } + columns = append(columns, aggregation...) + selector.Select(columns...) + } + selector.GroupBy(selector.Columns(*_g.flds...)...) + if err := selector.Err(); err != nil { + return err + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _g.build.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} + +// LighthouseSelect is the builder for selecting fields of Lighthouse entities. +type LighthouseSelect struct { + *LighthouseQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *LighthouseSelect) Aggregate(fns ...AggregateFunc) *LighthouseSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *LighthouseSelect) Scan(ctx context.Context, v any) error { + ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect) + if err := _s.prepareQuery(ctx); err != nil { + return err + } + return scanWithInterceptors[*LighthouseQuery, *LighthouseSelect](ctx, _s.LighthouseQuery, _s, _s.inters, v) +} + +func (_s *LighthouseSelect) sqlScan(ctx context.Context, root *LighthouseQuery, v any) error { + selector := root.sqlQuery(ctx) + aggregation := make([]string, 0, len(_s.fns)) + for _, fn := range _s.fns { + aggregation = append(aggregation, fn(selector)) + } + switch n := len(*_s.selector.flds); { + case n == 0 && len(aggregation) > 0: + selector.Select(aggregation...) + case n != 0 && len(aggregation) > 0: + selector.AppendSelect(aggregation...) + } + rows := &sql.Rows{} + query, args := selector.Query() + if err := _s.driver.Query(ctx, query, args, rows); err != nil { + return err + } + defer rows.Close() + return sql.ScanSlice(rows, v) +} diff --git a/westport/db/ent/lighthouse_update.go b/westport/db/ent/lighthouse_update.go new file mode 100644 index 0000000..6888fcc --- /dev/null +++ b/westport/db/ent/lighthouse_update.go @@ -0,0 +1,432 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/sprisa/west/westport/db/ent/host" + "github.com/sprisa/west/westport/db/ent/lighthouse" + "github.com/sprisa/west/westport/db/ent/predicate" + "github.com/sprisa/west/westport/db/helpers" +) + +// LighthouseUpdate is the builder for updating Lighthouse entities. +type LighthouseUpdate struct { + config + hooks []Hook + mutation *LighthouseMutation +} + +// Where appends a list predicates to the LighthouseUpdate builder. +func (_u *LighthouseUpdate) Where(ps ...predicate.Lighthouse) *LighthouseUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetUpdatedTime sets the "updated_time" field. +func (_u *LighthouseUpdate) SetUpdatedTime(v time.Time) *LighthouseUpdate { + _u.mutation.SetUpdatedTime(v) + return _u +} + +// SetEndpoint sets the "endpoint" field. +func (_u *LighthouseUpdate) SetEndpoint(v string) *LighthouseUpdate { + _u.mutation.SetEndpoint(v) + return _u +} + +// SetNillableEndpoint sets the "endpoint" field if the given value is not nil. +func (_u *LighthouseUpdate) SetNillableEndpoint(v *string) *LighthouseUpdate { + if v != nil { + _u.SetEndpoint(*v) + } + return _u +} + +// SetCertificate sets the "certificate" field. +func (_u *LighthouseUpdate) SetCertificate(v helpers.EncryptedBytes) *LighthouseUpdate { + _u.mutation.SetCertificate(v) + return _u +} + +// SetKey sets the "key" field. +func (_u *LighthouseUpdate) SetKey(v helpers.EncryptedBytes) *LighthouseUpdate { + _u.mutation.SetKey(v) + return _u +} + +// SetAPIEndpoint sets the "api_endpoint" field. +func (_u *LighthouseUpdate) SetAPIEndpoint(v string) *LighthouseUpdate { + _u.mutation.SetAPIEndpoint(v) + return _u +} + +// SetNillableAPIEndpoint sets the "api_endpoint" field if the given value is not nil. +func (_u *LighthouseUpdate) SetNillableAPIEndpoint(v *string) *LighthouseUpdate { + if v != nil { + _u.SetAPIEndpoint(*v) + } + return _u +} + +// SetHostID sets the "host" edge to the Host entity by ID. +func (_u *LighthouseUpdate) SetHostID(id int) *LighthouseUpdate { + _u.mutation.SetHostID(id) + return _u +} + +// SetHost sets the "host" edge to the Host entity. +func (_u *LighthouseUpdate) SetHost(v *Host) *LighthouseUpdate { + return _u.SetHostID(v.ID) +} + +// Mutation returns the LighthouseMutation object of the builder. +func (_u *LighthouseUpdate) Mutation() *LighthouseMutation { + return _u.mutation +} + +// ClearHost clears the "host" edge to the Host entity. +func (_u *LighthouseUpdate) ClearHost() *LighthouseUpdate { + _u.mutation.ClearHost() + return _u +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *LighthouseUpdate) Save(ctx context.Context) (int, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *LighthouseUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *LighthouseUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *LighthouseUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *LighthouseUpdate) defaults() { + if _, ok := _u.mutation.UpdatedTime(); !ok { + v := lighthouse.UpdateDefaultUpdatedTime() + _u.mutation.SetUpdatedTime(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *LighthouseUpdate) check() error { + if _u.mutation.HostCleared() && len(_u.mutation.HostIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "Lighthouse.host"`) + } + return nil +} + +func (_u *LighthouseUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(lighthouse.Table, lighthouse.Columns, sqlgraph.NewFieldSpec(lighthouse.FieldID, field.TypeInt)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.UpdatedTime(); ok { + _spec.SetField(lighthouse.FieldUpdatedTime, field.TypeTime, value) + } + if value, ok := _u.mutation.Endpoint(); ok { + _spec.SetField(lighthouse.FieldEndpoint, field.TypeString, value) + } + if value, ok := _u.mutation.Certificate(); ok { + _spec.SetField(lighthouse.FieldCertificate, field.TypeBytes, value) + } + if value, ok := _u.mutation.Key(); ok { + _spec.SetField(lighthouse.FieldKey, field.TypeBytes, value) + } + if value, ok := _u.mutation.APIEndpoint(); ok { + _spec.SetField(lighthouse.FieldAPIEndpoint, field.TypeString, value) + } + if _u.mutation.HostCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: lighthouse.HostTable, + Columns: []string{lighthouse.HostColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(host.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.HostIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: lighthouse.HostTable, + Columns: []string{lighthouse.HostColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(host.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{lighthouse.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// LighthouseUpdateOne is the builder for updating a single Lighthouse entity. +type LighthouseUpdateOne struct { + config + fields []string + hooks []Hook + mutation *LighthouseMutation +} + +// SetUpdatedTime sets the "updated_time" field. +func (_u *LighthouseUpdateOne) SetUpdatedTime(v time.Time) *LighthouseUpdateOne { + _u.mutation.SetUpdatedTime(v) + return _u +} + +// SetEndpoint sets the "endpoint" field. +func (_u *LighthouseUpdateOne) SetEndpoint(v string) *LighthouseUpdateOne { + _u.mutation.SetEndpoint(v) + return _u +} + +// SetNillableEndpoint sets the "endpoint" field if the given value is not nil. +func (_u *LighthouseUpdateOne) SetNillableEndpoint(v *string) *LighthouseUpdateOne { + if v != nil { + _u.SetEndpoint(*v) + } + return _u +} + +// SetCertificate sets the "certificate" field. +func (_u *LighthouseUpdateOne) SetCertificate(v helpers.EncryptedBytes) *LighthouseUpdateOne { + _u.mutation.SetCertificate(v) + return _u +} + +// SetKey sets the "key" field. +func (_u *LighthouseUpdateOne) SetKey(v helpers.EncryptedBytes) *LighthouseUpdateOne { + _u.mutation.SetKey(v) + return _u +} + +// SetAPIEndpoint sets the "api_endpoint" field. +func (_u *LighthouseUpdateOne) SetAPIEndpoint(v string) *LighthouseUpdateOne { + _u.mutation.SetAPIEndpoint(v) + return _u +} + +// SetNillableAPIEndpoint sets the "api_endpoint" field if the given value is not nil. +func (_u *LighthouseUpdateOne) SetNillableAPIEndpoint(v *string) *LighthouseUpdateOne { + if v != nil { + _u.SetAPIEndpoint(*v) + } + return _u +} + +// SetHostID sets the "host" edge to the Host entity by ID. +func (_u *LighthouseUpdateOne) SetHostID(id int) *LighthouseUpdateOne { + _u.mutation.SetHostID(id) + return _u +} + +// SetHost sets the "host" edge to the Host entity. +func (_u *LighthouseUpdateOne) SetHost(v *Host) *LighthouseUpdateOne { + return _u.SetHostID(v.ID) +} + +// Mutation returns the LighthouseMutation object of the builder. +func (_u *LighthouseUpdateOne) Mutation() *LighthouseMutation { + return _u.mutation +} + +// ClearHost clears the "host" edge to the Host entity. +func (_u *LighthouseUpdateOne) ClearHost() *LighthouseUpdateOne { + _u.mutation.ClearHost() + return _u +} + +// Where appends a list predicates to the LighthouseUpdate builder. +func (_u *LighthouseUpdateOne) Where(ps ...predicate.Lighthouse) *LighthouseUpdateOne { + _u.mutation.Where(ps...) + return _u +} + +// Select allows selecting one or more fields (columns) of the returned entity. +// The default is selecting all fields defined in the entity schema. +func (_u *LighthouseUpdateOne) Select(field string, fields ...string) *LighthouseUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated Lighthouse entity. +func (_u *LighthouseUpdateOne) Save(ctx context.Context) (*Lighthouse, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *LighthouseUpdateOne) SaveX(ctx context.Context) *Lighthouse { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *LighthouseUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *LighthouseUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *LighthouseUpdateOne) defaults() { + if _, ok := _u.mutation.UpdatedTime(); !ok { + v := lighthouse.UpdateDefaultUpdatedTime() + _u.mutation.SetUpdatedTime(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *LighthouseUpdateOne) check() error { + if _u.mutation.HostCleared() && len(_u.mutation.HostIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "Lighthouse.host"`) + } + return nil +} + +func (_u *LighthouseUpdateOne) sqlSave(ctx context.Context) (_node *Lighthouse, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(lighthouse.Table, lighthouse.Columns, sqlgraph.NewFieldSpec(lighthouse.FieldID, field.TypeInt)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Lighthouse.id" for update`)} + } + _spec.Node.ID.Value = id + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, 0, len(fields)) + _spec.Node.Columns = append(_spec.Node.Columns, lighthouse.FieldID) + for _, f := range fields { + if !lighthouse.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != lighthouse.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, f) + } + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if value, ok := _u.mutation.UpdatedTime(); ok { + _spec.SetField(lighthouse.FieldUpdatedTime, field.TypeTime, value) + } + if value, ok := _u.mutation.Endpoint(); ok { + _spec.SetField(lighthouse.FieldEndpoint, field.TypeString, value) + } + if value, ok := _u.mutation.Certificate(); ok { + _spec.SetField(lighthouse.FieldCertificate, field.TypeBytes, value) + } + if value, ok := _u.mutation.Key(); ok { + _spec.SetField(lighthouse.FieldKey, field.TypeBytes, value) + } + if value, ok := _u.mutation.APIEndpoint(); ok { + _spec.SetField(lighthouse.FieldAPIEndpoint, field.TypeString, value) + } + if _u.mutation.HostCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: lighthouse.HostTable, + Columns: []string{lighthouse.HostColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(host.FieldID, field.TypeInt), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.HostIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: lighthouse.HostTable, + Columns: []string{lighthouse.HostColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(host.FieldID, field.TypeInt), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + _node = &Lighthouse{config: _u.config} + _spec.Assign = _node.assignValues + _spec.ScanValues = _node.scanValues + if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{lighthouse.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return nil, err + } + _u.mutation.done = true + return _node, nil +} diff --git a/westport/db/ent/migrate/schema.go b/westport/db/ent/migrate/schema.go index 70ba7ce..e8b5598 100644 --- a/westport/db/ent/migrate/schema.go +++ b/westport/db/ent/migrate/schema.go @@ -17,12 +17,21 @@ var ( {Name: "ip", Type: field.TypeUint32}, {Name: "leased_access_token", Type: field.TypeString, Nullable: true}, {Name: "token", Type: field.TypeBytes}, + {Name: "device_host", Type: field.TypeInt}, } // DevicesTable holds the schema information for the "devices" table. DevicesTable = &schema.Table{ Name: "devices", Columns: DevicesColumns, PrimaryKey: []*schema.Column{DevicesColumns[0]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "devices_hosts_host", + Columns: []*schema.Column{DevicesColumns[7]}, + RefColumns: []*schema.Column{HostsColumns[0]}, + OnDelete: schema.NoAction, + }, + }, Indexes: []*schema.Index{ { Name: "device_ip", @@ -34,10 +43,58 @@ var ( Unique: true, Columns: []*schema.Column{DevicesColumns[3]}, }, + }, + } + // HostsColumns holds the columns for the "hosts" table. + HostsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeInt, Increment: true}, + {Name: "created_time", Type: field.TypeTime}, + {Name: "updated_time", Type: field.TypeTime}, + {Name: "ip", Type: field.TypeUint32}, + } + // HostsTable holds the schema information for the "hosts" table. + HostsTable = &schema.Table{ + Name: "hosts", + Columns: HostsColumns, + PrimaryKey: []*schema.Column{HostsColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "host_ip", + Unique: true, + Columns: []*schema.Column{HostsColumns[3]}, + }, + }, + } + // LighthousesColumns holds the columns for the "lighthouses" table. + LighthousesColumns = []*schema.Column{ + {Name: "id", Type: field.TypeInt, Increment: true}, + {Name: "created_time", Type: field.TypeTime}, + {Name: "updated_time", Type: field.TypeTime}, + {Name: "ip", Type: field.TypeUint32}, + {Name: "endpoint", Type: field.TypeString}, + {Name: "certificate", Type: field.TypeBytes}, + {Name: "key", Type: field.TypeBytes}, + {Name: "api_endpoint", Type: field.TypeString}, + {Name: "lighthouse_host", Type: field.TypeInt}, + } + // LighthousesTable holds the schema information for the "lighthouses" table. + LighthousesTable = &schema.Table{ + Name: "lighthouses", + Columns: LighthousesColumns, + PrimaryKey: []*schema.Column{LighthousesColumns[0]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "lighthouses_hosts_host", + Columns: []*schema.Column{LighthousesColumns[8]}, + RefColumns: []*schema.Column{HostsColumns[0]}, + OnDelete: schema.NoAction, + }, + }, + Indexes: []*schema.Index{ { - Name: "device_token", + Name: "lighthouse_ip", Unique: true, - Columns: []*schema.Column{DevicesColumns[6]}, + Columns: []*schema.Column{LighthousesColumns[3]}, }, }, } @@ -46,14 +103,12 @@ var ( {Name: "id", Type: field.TypeInt, Increment: true}, {Name: "created_time", Type: field.TypeTime}, {Name: "updated_time", Type: field.TypeTime}, + {Name: "network_id", Type: field.TypeString, Default: "default"}, {Name: "domain_zone", Type: field.TypeString, Nullable: true}, {Name: "cipher", Type: field.TypeString, Default: "aes"}, {Name: "ca_crt", Type: field.TypeBytes}, {Name: "ca_key", Type: field.TypeBytes}, - {Name: "lighthouse_crt", Type: field.TypeBytes}, - {Name: "lighthouse_key", Type: field.TypeBytes}, {Name: "cidr", Type: field.TypeString}, - {Name: "port_overlay_ip", Type: field.TypeUint32}, {Name: "letsencrypt_registration", Type: field.TypeBytes, Nullable: true}, {Name: "tls_cert", Type: field.TypeBytes, Nullable: true}, {Name: "tls_cert_key", Type: field.TypeBytes, Nullable: true}, @@ -63,13 +118,24 @@ var ( Name: "settings", Columns: SettingsColumns, PrimaryKey: []*schema.Column{SettingsColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "settings_network_id", + Unique: true, + Columns: []*schema.Column{SettingsColumns[3]}, + }, + }, } // Tables holds all the tables in the schema. Tables = []*schema.Table{ DevicesTable, + HostsTable, + LighthousesTable, SettingsTable, } ) func init() { + DevicesTable.ForeignKeys[0].RefTable = HostsTable + LighthousesTable.ForeignKeys[0].RefTable = HostsTable } diff --git a/westport/db/ent/mutation.go b/westport/db/ent/mutation.go index 90255e2..bf84f09 100644 --- a/westport/db/ent/mutation.go +++ b/westport/db/ent/mutation.go @@ -13,6 +13,8 @@ import ( "entgo.io/ent/dialect/sql" "github.com/sprisa/west/util/ipconv" "github.com/sprisa/west/westport/db/ent/device" + "github.com/sprisa/west/westport/db/ent/host" + "github.com/sprisa/west/westport/db/ent/lighthouse" "github.com/sprisa/west/westport/db/ent/predicate" "github.com/sprisa/west/westport/db/ent/settings" "github.com/sprisa/west/westport/db/helpers" @@ -27,8 +29,10 @@ const ( OpUpdateOne = ent.OpUpdateOne // Node types. - TypeDevice = "Device" - TypeSettings = "Settings" + TypeDevice = "Device" + TypeHost = "Host" + TypeLighthouse = "Lighthouse" + TypeSettings = "Settings" ) // DeviceMutation represents an operation that mutates the Device nodes in the graph. @@ -45,6 +49,8 @@ type DeviceMutation struct { leased_access_token *string token *helpers.EncryptedBytes clearedFields map[string]struct{} + host *int + clearedhost bool done bool oldValue func(context.Context) (*Device, error) predicates []predicate.Device @@ -397,6 +403,45 @@ func (m *DeviceMutation) ResetToken() { m.token = nil } +// SetHostID sets the "host" edge to the Host entity by id. +func (m *DeviceMutation) SetHostID(id int) { + m.host = &id +} + +// ClearHost clears the "host" edge to the Host entity. +func (m *DeviceMutation) ClearHost() { + m.clearedhost = true +} + +// HostCleared reports if the "host" edge to the Host entity was cleared. +func (m *DeviceMutation) HostCleared() bool { + return m.clearedhost +} + +// HostID returns the "host" edge ID in the mutation. +func (m *DeviceMutation) HostID() (id int, exists bool) { + if m.host != nil { + return *m.host, true + } + return +} + +// HostIDs returns the "host" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// HostID instead. It exists only for internal usage by the builders. +func (m *DeviceMutation) HostIDs() (ids []int) { + if id := m.host; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetHost resets all changes to the "host" edge. +func (m *DeviceMutation) ResetHost() { + m.host = nil + m.clearedhost = false +} + // Where appends a list predicates to the DeviceMutation builder. func (m *DeviceMutation) Where(ps ...predicate.Device) { m.predicates = append(m.predicates, ps...) @@ -422,33 +467,1251 @@ func (m *DeviceMutation) SetOp(op Op) { m.op = op } -// Type returns the node type of this mutation (Device). -func (m *DeviceMutation) Type() string { +// Type returns the node type of this mutation (Device). +func (m *DeviceMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *DeviceMutation) Fields() []string { + fields := make([]string, 0, 6) + if m.created_time != nil { + fields = append(fields, device.FieldCreatedTime) + } + if m.updated_time != nil { + fields = append(fields, device.FieldUpdatedTime) + } + if m.name != nil { + fields = append(fields, device.FieldName) + } + if m.ip != nil { + fields = append(fields, device.FieldIP) + } + if m.leased_access_token != nil { + fields = append(fields, device.FieldLeasedAccessToken) + } + if m.token != nil { + fields = append(fields, device.FieldToken) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *DeviceMutation) Field(name string) (ent.Value, bool) { + switch name { + case device.FieldCreatedTime: + return m.CreatedTime() + case device.FieldUpdatedTime: + return m.UpdatedTime() + case device.FieldName: + return m.Name() + case device.FieldIP: + return m.IP() + case device.FieldLeasedAccessToken: + return m.LeasedAccessToken() + case device.FieldToken: + return m.Token() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *DeviceMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case device.FieldCreatedTime: + return m.OldCreatedTime(ctx) + case device.FieldUpdatedTime: + return m.OldUpdatedTime(ctx) + case device.FieldName: + return m.OldName(ctx) + case device.FieldIP: + return m.OldIP(ctx) + case device.FieldLeasedAccessToken: + return m.OldLeasedAccessToken(ctx) + case device.FieldToken: + return m.OldToken(ctx) + } + return nil, fmt.Errorf("unknown Device field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *DeviceMutation) SetField(name string, value ent.Value) error { + switch name { + case device.FieldCreatedTime: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedTime(v) + return nil + case device.FieldUpdatedTime: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedTime(v) + return nil + case device.FieldName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetName(v) + return nil + case device.FieldIP: + v, ok := value.(ipconv.IP) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetIP(v) + return nil + case device.FieldLeasedAccessToken: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLeasedAccessToken(v) + return nil + case device.FieldToken: + v, ok := value.(helpers.EncryptedBytes) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetToken(v) + return nil + } + return fmt.Errorf("unknown Device field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *DeviceMutation) AddedFields() []string { + var fields []string + if m.addip != nil { + fields = append(fields, device.FieldIP) + } + return fields +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *DeviceMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case device.FieldIP: + return m.AddedIP() + } + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *DeviceMutation) AddField(name string, value ent.Value) error { + switch name { + case device.FieldIP: + v, ok := value.(ipconv.IP) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddIP(v) + return nil + } + return fmt.Errorf("unknown Device numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *DeviceMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(device.FieldLeasedAccessToken) { + fields = append(fields, device.FieldLeasedAccessToken) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *DeviceMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *DeviceMutation) ClearField(name string) error { + switch name { + case device.FieldLeasedAccessToken: + m.ClearLeasedAccessToken() + return nil + } + return fmt.Errorf("unknown Device nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *DeviceMutation) ResetField(name string) error { + switch name { + case device.FieldCreatedTime: + m.ResetCreatedTime() + return nil + case device.FieldUpdatedTime: + m.ResetUpdatedTime() + return nil + case device.FieldName: + m.ResetName() + return nil + case device.FieldIP: + m.ResetIP() + return nil + case device.FieldLeasedAccessToken: + m.ResetLeasedAccessToken() + return nil + case device.FieldToken: + m.ResetToken() + return nil + } + return fmt.Errorf("unknown Device field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *DeviceMutation) AddedEdges() []string { + edges := make([]string, 0, 1) + if m.host != nil { + edges = append(edges, device.EdgeHost) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *DeviceMutation) AddedIDs(name string) []ent.Value { + switch name { + case device.EdgeHost: + if id := m.host; id != nil { + return []ent.Value{*id} + } + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *DeviceMutation) RemovedEdges() []string { + edges := make([]string, 0, 1) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *DeviceMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *DeviceMutation) ClearedEdges() []string { + edges := make([]string, 0, 1) + if m.clearedhost { + edges = append(edges, device.EdgeHost) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *DeviceMutation) EdgeCleared(name string) bool { + switch name { + case device.EdgeHost: + return m.clearedhost + } + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *DeviceMutation) ClearEdge(name string) error { + switch name { + case device.EdgeHost: + m.ClearHost() + return nil + } + return fmt.Errorf("unknown Device unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *DeviceMutation) ResetEdge(name string) error { + switch name { + case device.EdgeHost: + m.ResetHost() + return nil + } + return fmt.Errorf("unknown Device edge %s", name) +} + +// HostMutation represents an operation that mutates the Host nodes in the graph. +type HostMutation struct { + config + op Op + typ string + id *int + created_time *time.Time + updated_time *time.Time + ip *ipconv.IP + addip *ipconv.IP + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*Host, error) + predicates []predicate.Host +} + +var _ ent.Mutation = (*HostMutation)(nil) + +// hostOption allows management of the mutation configuration using functional options. +type hostOption func(*HostMutation) + +// newHostMutation creates new mutation for the Host entity. +func newHostMutation(c config, op Op, opts ...hostOption) *HostMutation { + m := &HostMutation{ + config: c, + op: op, + typ: TypeHost, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withHostID sets the ID field of the mutation. +func withHostID(id int) hostOption { + return func(m *HostMutation) { + var ( + err error + once sync.Once + value *Host + ) + m.oldValue = func(ctx context.Context) (*Host, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().Host.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withHost sets the old Host of the mutation. +func withHost(node *Host) hostOption { + return func(m *HostMutation) { + m.oldValue = func(context.Context) (*Host, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m HostMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m HostMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *HostMutation) ID() (id int, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *HostMutation) IDs(ctx context.Context) ([]int, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []int{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().Host.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetCreatedTime sets the "created_time" field. +func (m *HostMutation) SetCreatedTime(t time.Time) { + m.created_time = &t +} + +// CreatedTime returns the value of the "created_time" field in the mutation. +func (m *HostMutation) CreatedTime() (r time.Time, exists bool) { + v := m.created_time + if v == nil { + return + } + return *v, true +} + +// OldCreatedTime returns the old "created_time" field's value of the Host entity. +// If the Host object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *HostMutation) OldCreatedTime(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedTime is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedTime requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedTime: %w", err) + } + return oldValue.CreatedTime, nil +} + +// ResetCreatedTime resets all changes to the "created_time" field. +func (m *HostMutation) ResetCreatedTime() { + m.created_time = nil +} + +// SetUpdatedTime sets the "updated_time" field. +func (m *HostMutation) SetUpdatedTime(t time.Time) { + m.updated_time = &t +} + +// UpdatedTime returns the value of the "updated_time" field in the mutation. +func (m *HostMutation) UpdatedTime() (r time.Time, exists bool) { + v := m.updated_time + if v == nil { + return + } + return *v, true +} + +// OldUpdatedTime returns the old "updated_time" field's value of the Host entity. +// If the Host object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *HostMutation) OldUpdatedTime(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedTime is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedTime requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedTime: %w", err) + } + return oldValue.UpdatedTime, nil +} + +// ResetUpdatedTime resets all changes to the "updated_time" field. +func (m *HostMutation) ResetUpdatedTime() { + m.updated_time = nil +} + +// SetIP sets the "ip" field. +func (m *HostMutation) SetIP(i ipconv.IP) { + m.ip = &i + m.addip = nil +} + +// IP returns the value of the "ip" field in the mutation. +func (m *HostMutation) IP() (r ipconv.IP, exists bool) { + v := m.ip + if v == nil { + return + } + return *v, true +} + +// OldIP returns the old "ip" field's value of the Host entity. +// If the Host object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *HostMutation) OldIP(ctx context.Context) (v ipconv.IP, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldIP is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldIP requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldIP: %w", err) + } + return oldValue.IP, nil +} + +// AddIP adds i to the "ip" field. +func (m *HostMutation) AddIP(i ipconv.IP) { + if m.addip != nil { + *m.addip += i + } else { + m.addip = &i + } +} + +// AddedIP returns the value that was added to the "ip" field in this mutation. +func (m *HostMutation) AddedIP() (r ipconv.IP, exists bool) { + v := m.addip + if v == nil { + return + } + return *v, true +} + +// ResetIP resets all changes to the "ip" field. +func (m *HostMutation) ResetIP() { + m.ip = nil + m.addip = nil +} + +// Where appends a list predicates to the HostMutation builder. +func (m *HostMutation) Where(ps ...predicate.Host) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the HostMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *HostMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.Host, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *HostMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *HostMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (Host). +func (m *HostMutation) Type() string { + return m.typ +} + +// Fields returns all fields that were changed during this mutation. Note that in +// order to get all numeric fields that were incremented/decremented, call +// AddedFields(). +func (m *HostMutation) Fields() []string { + fields := make([]string, 0, 3) + if m.created_time != nil { + fields = append(fields, host.FieldCreatedTime) + } + if m.updated_time != nil { + fields = append(fields, host.FieldUpdatedTime) + } + if m.ip != nil { + fields = append(fields, host.FieldIP) + } + return fields +} + +// Field returns the value of a field with the given name. The second boolean +// return value indicates that this field was not set, or was not defined in the +// schema. +func (m *HostMutation) Field(name string) (ent.Value, bool) { + switch name { + case host.FieldCreatedTime: + return m.CreatedTime() + case host.FieldUpdatedTime: + return m.UpdatedTime() + case host.FieldIP: + return m.IP() + } + return nil, false +} + +// OldField returns the old value of the field from the database. An error is +// returned if the mutation operation is not UpdateOne, or the query to the +// database failed. +func (m *HostMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case host.FieldCreatedTime: + return m.OldCreatedTime(ctx) + case host.FieldUpdatedTime: + return m.OldUpdatedTime(ctx) + case host.FieldIP: + return m.OldIP(ctx) + } + return nil, fmt.Errorf("unknown Host field %s", name) +} + +// SetField sets the value of a field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *HostMutation) SetField(name string, value ent.Value) error { + switch name { + case host.FieldCreatedTime: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedTime(v) + return nil + case host.FieldUpdatedTime: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedTime(v) + return nil + case host.FieldIP: + v, ok := value.(ipconv.IP) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetIP(v) + return nil + } + return fmt.Errorf("unknown Host field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *HostMutation) AddedFields() []string { + var fields []string + if m.addip != nil { + fields = append(fields, host.FieldIP) + } + return fields +} + +// AddedField returns the numeric value that was incremented/decremented on a field +// with the given name. The second boolean return value indicates that this field +// was not set, or was not defined in the schema. +func (m *HostMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case host.FieldIP: + return m.AddedIP() + } + return nil, false +} + +// AddField adds the value to the field with the given name. It returns an error if +// the field is not defined in the schema, or if the type mismatched the field +// type. +func (m *HostMutation) AddField(name string, value ent.Value) error { + switch name { + case host.FieldIP: + v, ok := value.(ipconv.IP) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddIP(v) + return nil + } + return fmt.Errorf("unknown Host numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *HostMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *HostMutation) FieldCleared(name string) bool { + _, ok := m.clearedFields[name] + return ok +} + +// ClearField clears the value of the field with the given name. It returns an +// error if the field is not defined in the schema. +func (m *HostMutation) ClearField(name string) error { + return fmt.Errorf("unknown Host nullable field %s", name) +} + +// ResetField resets all changes in the mutation for the field with the given name. +// It returns an error if the field is not defined in the schema. +func (m *HostMutation) ResetField(name string) error { + switch name { + case host.FieldCreatedTime: + m.ResetCreatedTime() + return nil + case host.FieldUpdatedTime: + m.ResetUpdatedTime() + return nil + case host.FieldIP: + m.ResetIP() + return nil + } + return fmt.Errorf("unknown Host field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *HostMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *HostMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *HostMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *HostMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *HostMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *HostMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *HostMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown Host unique edge %s", name) +} + +// ResetEdge resets all changes to the edge with the given name in this mutation. +// It returns an error if the edge is not defined in the schema. +func (m *HostMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown Host edge %s", name) +} + +// LighthouseMutation represents an operation that mutates the Lighthouse nodes in the graph. +type LighthouseMutation struct { + config + op Op + typ string + id *int + created_time *time.Time + updated_time *time.Time + ip *ipconv.IP + addip *ipconv.IP + endpoint *string + certificate *helpers.EncryptedBytes + key *helpers.EncryptedBytes + api_endpoint *string + clearedFields map[string]struct{} + host *int + clearedhost bool + done bool + oldValue func(context.Context) (*Lighthouse, error) + predicates []predicate.Lighthouse +} + +var _ ent.Mutation = (*LighthouseMutation)(nil) + +// lighthouseOption allows management of the mutation configuration using functional options. +type lighthouseOption func(*LighthouseMutation) + +// newLighthouseMutation creates new mutation for the Lighthouse entity. +func newLighthouseMutation(c config, op Op, opts ...lighthouseOption) *LighthouseMutation { + m := &LighthouseMutation{ + config: c, + op: op, + typ: TypeLighthouse, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withLighthouseID sets the ID field of the mutation. +func withLighthouseID(id int) lighthouseOption { + return func(m *LighthouseMutation) { + var ( + err error + once sync.Once + value *Lighthouse + ) + m.oldValue = func(ctx context.Context) (*Lighthouse, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().Lighthouse.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withLighthouse sets the old Lighthouse of the mutation. +func withLighthouse(node *Lighthouse) lighthouseOption { + return func(m *LighthouseMutation) { + m.oldValue = func(context.Context) (*Lighthouse, error) { + return node, nil + } + m.id = &node.ID + } +} + +// Client returns a new `ent.Client` from the mutation. If the mutation was +// executed in a transaction (ent.Tx), a transactional client is returned. +func (m LighthouseMutation) Client() *Client { + client := &Client{config: m.config} + client.init() + return client +} + +// Tx returns an `ent.Tx` for mutations that were executed in transactions; +// it returns an error otherwise. +func (m LighthouseMutation) Tx() (*Tx, error) { + if _, ok := m.driver.(*txDriver); !ok { + return nil, errors.New("ent: mutation is not running in a transaction") + } + tx := &Tx{config: m.config} + tx.init() + return tx, nil +} + +// ID returns the ID value in the mutation. Note that the ID is only available +// if it was provided to the builder or after it was returned from the database. +func (m *LighthouseMutation) ID() (id int, exists bool) { + if m.id == nil { + return + } + return *m.id, true +} + +// IDs queries the database and returns the entity ids that match the mutation's predicate. +// That means, if the mutation is applied within a transaction with an isolation level such +// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated +// or updated by the mutation. +func (m *LighthouseMutation) IDs(ctx context.Context) ([]int, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []int{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().Lighthouse.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetCreatedTime sets the "created_time" field. +func (m *LighthouseMutation) SetCreatedTime(t time.Time) { + m.created_time = &t +} + +// CreatedTime returns the value of the "created_time" field in the mutation. +func (m *LighthouseMutation) CreatedTime() (r time.Time, exists bool) { + v := m.created_time + if v == nil { + return + } + return *v, true +} + +// OldCreatedTime returns the old "created_time" field's value of the Lighthouse entity. +// If the Lighthouse object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *LighthouseMutation) OldCreatedTime(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedTime is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedTime requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedTime: %w", err) + } + return oldValue.CreatedTime, nil +} + +// ResetCreatedTime resets all changes to the "created_time" field. +func (m *LighthouseMutation) ResetCreatedTime() { + m.created_time = nil +} + +// SetUpdatedTime sets the "updated_time" field. +func (m *LighthouseMutation) SetUpdatedTime(t time.Time) { + m.updated_time = &t +} + +// UpdatedTime returns the value of the "updated_time" field in the mutation. +func (m *LighthouseMutation) UpdatedTime() (r time.Time, exists bool) { + v := m.updated_time + if v == nil { + return + } + return *v, true +} + +// OldUpdatedTime returns the old "updated_time" field's value of the Lighthouse entity. +// If the Lighthouse object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *LighthouseMutation) OldUpdatedTime(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedTime is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedTime requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedTime: %w", err) + } + return oldValue.UpdatedTime, nil +} + +// ResetUpdatedTime resets all changes to the "updated_time" field. +func (m *LighthouseMutation) ResetUpdatedTime() { + m.updated_time = nil +} + +// SetIP sets the "ip" field. +func (m *LighthouseMutation) SetIP(i ipconv.IP) { + m.ip = &i + m.addip = nil +} + +// IP returns the value of the "ip" field in the mutation. +func (m *LighthouseMutation) IP() (r ipconv.IP, exists bool) { + v := m.ip + if v == nil { + return + } + return *v, true +} + +// OldIP returns the old "ip" field's value of the Lighthouse entity. +// If the Lighthouse object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *LighthouseMutation) OldIP(ctx context.Context) (v ipconv.IP, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldIP is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldIP requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldIP: %w", err) + } + return oldValue.IP, nil +} + +// AddIP adds i to the "ip" field. +func (m *LighthouseMutation) AddIP(i ipconv.IP) { + if m.addip != nil { + *m.addip += i + } else { + m.addip = &i + } +} + +// AddedIP returns the value that was added to the "ip" field in this mutation. +func (m *LighthouseMutation) AddedIP() (r ipconv.IP, exists bool) { + v := m.addip + if v == nil { + return + } + return *v, true +} + +// ResetIP resets all changes to the "ip" field. +func (m *LighthouseMutation) ResetIP() { + m.ip = nil + m.addip = nil +} + +// SetEndpoint sets the "endpoint" field. +func (m *LighthouseMutation) SetEndpoint(s string) { + m.endpoint = &s +} + +// Endpoint returns the value of the "endpoint" field in the mutation. +func (m *LighthouseMutation) Endpoint() (r string, exists bool) { + v := m.endpoint + if v == nil { + return + } + return *v, true +} + +// OldEndpoint returns the old "endpoint" field's value of the Lighthouse entity. +// If the Lighthouse object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *LighthouseMutation) OldEndpoint(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldEndpoint is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldEndpoint requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldEndpoint: %w", err) + } + return oldValue.Endpoint, nil +} + +// ResetEndpoint resets all changes to the "endpoint" field. +func (m *LighthouseMutation) ResetEndpoint() { + m.endpoint = nil +} + +// SetCertificate sets the "certificate" field. +func (m *LighthouseMutation) SetCertificate(hb helpers.EncryptedBytes) { + m.certificate = &hb +} + +// Certificate returns the value of the "certificate" field in the mutation. +func (m *LighthouseMutation) Certificate() (r helpers.EncryptedBytes, exists bool) { + v := m.certificate + if v == nil { + return + } + return *v, true +} + +// OldCertificate returns the old "certificate" field's value of the Lighthouse entity. +// If the Lighthouse object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *LighthouseMutation) OldCertificate(ctx context.Context) (v helpers.EncryptedBytes, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCertificate is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCertificate requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCertificate: %w", err) + } + return oldValue.Certificate, nil +} + +// ResetCertificate resets all changes to the "certificate" field. +func (m *LighthouseMutation) ResetCertificate() { + m.certificate = nil +} + +// SetKey sets the "key" field. +func (m *LighthouseMutation) SetKey(hb helpers.EncryptedBytes) { + m.key = &hb +} + +// Key returns the value of the "key" field in the mutation. +func (m *LighthouseMutation) Key() (r helpers.EncryptedBytes, exists bool) { + v := m.key + if v == nil { + return + } + return *v, true +} + +// OldKey returns the old "key" field's value of the Lighthouse entity. +// If the Lighthouse object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *LighthouseMutation) OldKey(ctx context.Context) (v helpers.EncryptedBytes, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldKey is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldKey requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldKey: %w", err) + } + return oldValue.Key, nil +} + +// ResetKey resets all changes to the "key" field. +func (m *LighthouseMutation) ResetKey() { + m.key = nil +} + +// SetAPIEndpoint sets the "api_endpoint" field. +func (m *LighthouseMutation) SetAPIEndpoint(s string) { + m.api_endpoint = &s +} + +// APIEndpoint returns the value of the "api_endpoint" field in the mutation. +func (m *LighthouseMutation) APIEndpoint() (r string, exists bool) { + v := m.api_endpoint + if v == nil { + return + } + return *v, true +} + +// OldAPIEndpoint returns the old "api_endpoint" field's value of the Lighthouse entity. +// If the Lighthouse object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *LighthouseMutation) OldAPIEndpoint(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldAPIEndpoint is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldAPIEndpoint requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldAPIEndpoint: %w", err) + } + return oldValue.APIEndpoint, nil +} + +// ResetAPIEndpoint resets all changes to the "api_endpoint" field. +func (m *LighthouseMutation) ResetAPIEndpoint() { + m.api_endpoint = nil +} + +// SetHostID sets the "host" edge to the Host entity by id. +func (m *LighthouseMutation) SetHostID(id int) { + m.host = &id +} + +// ClearHost clears the "host" edge to the Host entity. +func (m *LighthouseMutation) ClearHost() { + m.clearedhost = true +} + +// HostCleared reports if the "host" edge to the Host entity was cleared. +func (m *LighthouseMutation) HostCleared() bool { + return m.clearedhost +} + +// HostID returns the "host" edge ID in the mutation. +func (m *LighthouseMutation) HostID() (id int, exists bool) { + if m.host != nil { + return *m.host, true + } + return +} + +// HostIDs returns the "host" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// HostID instead. It exists only for internal usage by the builders. +func (m *LighthouseMutation) HostIDs() (ids []int) { + if id := m.host; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetHost resets all changes to the "host" edge. +func (m *LighthouseMutation) ResetHost() { + m.host = nil + m.clearedhost = false +} + +// Where appends a list predicates to the LighthouseMutation builder. +func (m *LighthouseMutation) Where(ps ...predicate.Lighthouse) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the LighthouseMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *LighthouseMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.Lighthouse, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *LighthouseMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *LighthouseMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (Lighthouse). +func (m *LighthouseMutation) Type() string { return m.typ } // Fields returns all fields that were changed during this mutation. Note that in // order to get all numeric fields that were incremented/decremented, call // AddedFields(). -func (m *DeviceMutation) Fields() []string { - fields := make([]string, 0, 6) +func (m *LighthouseMutation) Fields() []string { + fields := make([]string, 0, 7) if m.created_time != nil { - fields = append(fields, device.FieldCreatedTime) + fields = append(fields, lighthouse.FieldCreatedTime) } if m.updated_time != nil { - fields = append(fields, device.FieldUpdatedTime) - } - if m.name != nil { - fields = append(fields, device.FieldName) + fields = append(fields, lighthouse.FieldUpdatedTime) } if m.ip != nil { - fields = append(fields, device.FieldIP) + fields = append(fields, lighthouse.FieldIP) } - if m.leased_access_token != nil { - fields = append(fields, device.FieldLeasedAccessToken) + if m.endpoint != nil { + fields = append(fields, lighthouse.FieldEndpoint) } - if m.token != nil { - fields = append(fields, device.FieldToken) + if m.certificate != nil { + fields = append(fields, lighthouse.FieldCertificate) + } + if m.key != nil { + fields = append(fields, lighthouse.FieldKey) + } + if m.api_endpoint != nil { + fields = append(fields, lighthouse.FieldAPIEndpoint) } return fields } @@ -456,20 +1719,22 @@ func (m *DeviceMutation) Fields() []string { // Field returns the value of a field with the given name. The second boolean // return value indicates that this field was not set, or was not defined in the // schema. -func (m *DeviceMutation) Field(name string) (ent.Value, bool) { +func (m *LighthouseMutation) Field(name string) (ent.Value, bool) { switch name { - case device.FieldCreatedTime: + case lighthouse.FieldCreatedTime: return m.CreatedTime() - case device.FieldUpdatedTime: + case lighthouse.FieldUpdatedTime: return m.UpdatedTime() - case device.FieldName: - return m.Name() - case device.FieldIP: + case lighthouse.FieldIP: return m.IP() - case device.FieldLeasedAccessToken: - return m.LeasedAccessToken() - case device.FieldToken: - return m.Token() + case lighthouse.FieldEndpoint: + return m.Endpoint() + case lighthouse.FieldCertificate: + return m.Certificate() + case lighthouse.FieldKey: + return m.Key() + case lighthouse.FieldAPIEndpoint: + return m.APIEndpoint() } return nil, false } @@ -477,81 +1742,90 @@ func (m *DeviceMutation) Field(name string) (ent.Value, bool) { // OldField returns the old value of the field from the database. An error is // returned if the mutation operation is not UpdateOne, or the query to the // database failed. -func (m *DeviceMutation) OldField(ctx context.Context, name string) (ent.Value, error) { +func (m *LighthouseMutation) OldField(ctx context.Context, name string) (ent.Value, error) { switch name { - case device.FieldCreatedTime: + case lighthouse.FieldCreatedTime: return m.OldCreatedTime(ctx) - case device.FieldUpdatedTime: + case lighthouse.FieldUpdatedTime: return m.OldUpdatedTime(ctx) - case device.FieldName: - return m.OldName(ctx) - case device.FieldIP: + case lighthouse.FieldIP: return m.OldIP(ctx) - case device.FieldLeasedAccessToken: - return m.OldLeasedAccessToken(ctx) - case device.FieldToken: - return m.OldToken(ctx) + case lighthouse.FieldEndpoint: + return m.OldEndpoint(ctx) + case lighthouse.FieldCertificate: + return m.OldCertificate(ctx) + case lighthouse.FieldKey: + return m.OldKey(ctx) + case lighthouse.FieldAPIEndpoint: + return m.OldAPIEndpoint(ctx) } - return nil, fmt.Errorf("unknown Device field %s", name) + return nil, fmt.Errorf("unknown Lighthouse field %s", name) } // SetField sets the value of a field with the given name. It returns an error if // the field is not defined in the schema, or if the type mismatched the field // type. -func (m *DeviceMutation) SetField(name string, value ent.Value) error { +func (m *LighthouseMutation) SetField(name string, value ent.Value) error { switch name { - case device.FieldCreatedTime: + case lighthouse.FieldCreatedTime: v, ok := value.(time.Time) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetCreatedTime(v) return nil - case device.FieldUpdatedTime: + case lighthouse.FieldUpdatedTime: v, ok := value.(time.Time) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } m.SetUpdatedTime(v) return nil - case device.FieldName: - v, ok := value.(string) + case lighthouse.FieldIP: + v, ok := value.(ipconv.IP) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetName(v) + m.SetIP(v) return nil - case device.FieldIP: - v, ok := value.(ipconv.IP) + case lighthouse.FieldEndpoint: + v, ok := value.(string) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetIP(v) + m.SetEndpoint(v) return nil - case device.FieldLeasedAccessToken: - v, ok := value.(string) + case lighthouse.FieldCertificate: + v, ok := value.(helpers.EncryptedBytes) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetLeasedAccessToken(v) + m.SetCertificate(v) return nil - case device.FieldToken: + case lighthouse.FieldKey: v, ok := value.(helpers.EncryptedBytes) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) } - m.SetToken(v) + m.SetKey(v) + return nil + case lighthouse.FieldAPIEndpoint: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetAPIEndpoint(v) return nil } - return fmt.Errorf("unknown Device field %s", name) + return fmt.Errorf("unknown Lighthouse field %s", name) } // AddedFields returns all numeric fields that were incremented/decremented during // this mutation. -func (m *DeviceMutation) AddedFields() []string { +func (m *LighthouseMutation) AddedFields() []string { var fields []string if m.addip != nil { - fields = append(fields, device.FieldIP) + fields = append(fields, lighthouse.FieldIP) } return fields } @@ -559,9 +1833,9 @@ func (m *DeviceMutation) AddedFields() []string { // AddedField returns the numeric value that was incremented/decremented on a field // with the given name. The second boolean return value indicates that this field // was not set, or was not defined in the schema. -func (m *DeviceMutation) AddedField(name string) (ent.Value, bool) { +func (m *LighthouseMutation) AddedField(name string) (ent.Value, bool) { switch name { - case device.FieldIP: + case lighthouse.FieldIP: return m.AddedIP() } return nil, false @@ -570,9 +1844,9 @@ func (m *DeviceMutation) AddedField(name string) (ent.Value, bool) { // AddField adds the value to the field with the given name. It returns an error if // the field is not defined in the schema, or if the type mismatched the field // type. -func (m *DeviceMutation) AddField(name string, value ent.Value) error { +func (m *LighthouseMutation) AddField(name string, value ent.Value) error { switch name { - case device.FieldIP: + case lighthouse.FieldIP: v, ok := value.(ipconv.IP) if !ok { return fmt.Errorf("unexpected type %T for field %s", value, name) @@ -580,109 +1854,129 @@ func (m *DeviceMutation) AddField(name string, value ent.Value) error { m.AddIP(v) return nil } - return fmt.Errorf("unknown Device numeric field %s", name) + return fmt.Errorf("unknown Lighthouse numeric field %s", name) } // ClearedFields returns all nullable fields that were cleared during this // mutation. -func (m *DeviceMutation) ClearedFields() []string { - var fields []string - if m.FieldCleared(device.FieldLeasedAccessToken) { - fields = append(fields, device.FieldLeasedAccessToken) - } - return fields +func (m *LighthouseMutation) ClearedFields() []string { + return nil } // FieldCleared returns a boolean indicating if a field with the given name was // cleared in this mutation. -func (m *DeviceMutation) FieldCleared(name string) bool { +func (m *LighthouseMutation) FieldCleared(name string) bool { _, ok := m.clearedFields[name] return ok } // ClearField clears the value of the field with the given name. It returns an // error if the field is not defined in the schema. -func (m *DeviceMutation) ClearField(name string) error { - switch name { - case device.FieldLeasedAccessToken: - m.ClearLeasedAccessToken() - return nil - } - return fmt.Errorf("unknown Device nullable field %s", name) +func (m *LighthouseMutation) ClearField(name string) error { + return fmt.Errorf("unknown Lighthouse nullable field %s", name) } // ResetField resets all changes in the mutation for the field with the given name. // It returns an error if the field is not defined in the schema. -func (m *DeviceMutation) ResetField(name string) error { +func (m *LighthouseMutation) ResetField(name string) error { switch name { - case device.FieldCreatedTime: + case lighthouse.FieldCreatedTime: m.ResetCreatedTime() return nil - case device.FieldUpdatedTime: + case lighthouse.FieldUpdatedTime: m.ResetUpdatedTime() return nil - case device.FieldName: - m.ResetName() - return nil - case device.FieldIP: + case lighthouse.FieldIP: m.ResetIP() return nil - case device.FieldLeasedAccessToken: - m.ResetLeasedAccessToken() + case lighthouse.FieldEndpoint: + m.ResetEndpoint() return nil - case device.FieldToken: - m.ResetToken() + case lighthouse.FieldCertificate: + m.ResetCertificate() + return nil + case lighthouse.FieldKey: + m.ResetKey() + return nil + case lighthouse.FieldAPIEndpoint: + m.ResetAPIEndpoint() return nil } - return fmt.Errorf("unknown Device field %s", name) + return fmt.Errorf("unknown Lighthouse field %s", name) } // AddedEdges returns all edge names that were set/added in this mutation. -func (m *DeviceMutation) AddedEdges() []string { - edges := make([]string, 0, 0) +func (m *LighthouseMutation) AddedEdges() []string { + edges := make([]string, 0, 1) + if m.host != nil { + edges = append(edges, lighthouse.EdgeHost) + } return edges } // AddedIDs returns all IDs (to other nodes) that were added for the given edge // name in this mutation. -func (m *DeviceMutation) AddedIDs(name string) []ent.Value { +func (m *LighthouseMutation) AddedIDs(name string) []ent.Value { + switch name { + case lighthouse.EdgeHost: + if id := m.host; id != nil { + return []ent.Value{*id} + } + } return nil } // RemovedEdges returns all edge names that were removed in this mutation. -func (m *DeviceMutation) RemovedEdges() []string { - edges := make([]string, 0, 0) +func (m *LighthouseMutation) RemovedEdges() []string { + edges := make([]string, 0, 1) return edges } // RemovedIDs returns all IDs (to other nodes) that were removed for the edge with // the given name in this mutation. -func (m *DeviceMutation) RemovedIDs(name string) []ent.Value { +func (m *LighthouseMutation) RemovedIDs(name string) []ent.Value { return nil } // ClearedEdges returns all edge names that were cleared in this mutation. -func (m *DeviceMutation) ClearedEdges() []string { - edges := make([]string, 0, 0) +func (m *LighthouseMutation) ClearedEdges() []string { + edges := make([]string, 0, 1) + if m.clearedhost { + edges = append(edges, lighthouse.EdgeHost) + } return edges } // EdgeCleared returns a boolean which indicates if the edge with the given name // was cleared in this mutation. -func (m *DeviceMutation) EdgeCleared(name string) bool { +func (m *LighthouseMutation) EdgeCleared(name string) bool { + switch name { + case lighthouse.EdgeHost: + return m.clearedhost + } return false } // ClearEdge clears the value of the edge with the given name. It returns an error // if that edge is not defined in the schema. -func (m *DeviceMutation) ClearEdge(name string) error { - return fmt.Errorf("unknown Device unique edge %s", name) +func (m *LighthouseMutation) ClearEdge(name string) error { + switch name { + case lighthouse.EdgeHost: + m.ClearHost() + return nil + } + return fmt.Errorf("unknown Lighthouse unique edge %s", name) } // ResetEdge resets all changes to the edge with the given name in this mutation. // It returns an error if the edge is not defined in the schema. -func (m *DeviceMutation) ResetEdge(name string) error { - return fmt.Errorf("unknown Device edge %s", name) +func (m *LighthouseMutation) ResetEdge(name string) error { + switch name { + case lighthouse.EdgeHost: + m.ResetHost() + return nil + } + return fmt.Errorf("unknown Lighthouse edge %s", name) } // SettingsMutation represents an operation that mutates the Settings nodes in the graph. @@ -693,15 +1987,12 @@ type SettingsMutation struct { id *int created_time *time.Time updated_time *time.Time + network_id *string domain_zone *string cipher *string ca_crt *helpers.EncryptedBytes ca_key *helpers.EncryptedBytes - lighthouse_crt *helpers.EncryptedBytes - lighthouse_key *helpers.EncryptedBytes cidr *helpers.IpCidr - port_overlay_ip *ipconv.IP - addport_overlay_ip *ipconv.IP letsencrypt_registration *helpers.EncryptedBytes tls_cert *helpers.EncryptedBytes tls_cert_key *helpers.EncryptedBytes @@ -881,6 +2172,42 @@ func (m *SettingsMutation) ResetUpdatedTime() { m.updated_time = nil } +// SetNetworkID sets the "network_id" field. +func (m *SettingsMutation) SetNetworkID(s string) { + m.network_id = &s +} + +// NetworkID returns the value of the "network_id" field in the mutation. +func (m *SettingsMutation) NetworkID() (r string, exists bool) { + v := m.network_id + if v == nil { + return + } + return *v, true +} + +// OldNetworkID returns the old "network_id" field's value of the Settings entity. +// If the Settings object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *SettingsMutation) OldNetworkID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldNetworkID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldNetworkID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldNetworkID: %w", err) + } + return oldValue.NetworkID, nil +} + +// ResetNetworkID resets all changes to the "network_id" field. +func (m *SettingsMutation) ResetNetworkID() { + m.network_id = nil +} + // SetDomainZone sets the "domain_zone" field. func (m *SettingsMutation) SetDomainZone(s string) { m.domain_zone = &s @@ -1038,78 +2365,6 @@ func (m *SettingsMutation) ResetCaKey() { m.ca_key = nil } -// SetLighthouseCrt sets the "lighthouse_crt" field. -func (m *SettingsMutation) SetLighthouseCrt(hb helpers.EncryptedBytes) { - m.lighthouse_crt = &hb -} - -// LighthouseCrt returns the value of the "lighthouse_crt" field in the mutation. -func (m *SettingsMutation) LighthouseCrt() (r helpers.EncryptedBytes, exists bool) { - v := m.lighthouse_crt - if v == nil { - return - } - return *v, true -} - -// OldLighthouseCrt returns the old "lighthouse_crt" field's value of the Settings entity. -// If the Settings object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *SettingsMutation) OldLighthouseCrt(ctx context.Context) (v helpers.EncryptedBytes, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldLighthouseCrt is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldLighthouseCrt requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldLighthouseCrt: %w", err) - } - return oldValue.LighthouseCrt, nil -} - -// ResetLighthouseCrt resets all changes to the "lighthouse_crt" field. -func (m *SettingsMutation) ResetLighthouseCrt() { - m.lighthouse_crt = nil -} - -// SetLighthouseKey sets the "lighthouse_key" field. -func (m *SettingsMutation) SetLighthouseKey(hb helpers.EncryptedBytes) { - m.lighthouse_key = &hb -} - -// LighthouseKey returns the value of the "lighthouse_key" field in the mutation. -func (m *SettingsMutation) LighthouseKey() (r helpers.EncryptedBytes, exists bool) { - v := m.lighthouse_key - if v == nil { - return - } - return *v, true -} - -// OldLighthouseKey returns the old "lighthouse_key" field's value of the Settings entity. -// If the Settings object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *SettingsMutation) OldLighthouseKey(ctx context.Context) (v helpers.EncryptedBytes, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldLighthouseKey is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldLighthouseKey requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldLighthouseKey: %w", err) - } - return oldValue.LighthouseKey, nil -} - -// ResetLighthouseKey resets all changes to the "lighthouse_key" field. -func (m *SettingsMutation) ResetLighthouseKey() { - m.lighthouse_key = nil -} - // SetCidr sets the "cidr" field. func (m *SettingsMutation) SetCidr(hc helpers.IpCidr) { m.cidr = &hc @@ -1146,62 +2401,6 @@ func (m *SettingsMutation) ResetCidr() { m.cidr = nil } -// SetPortOverlayIP sets the "port_overlay_ip" field. -func (m *SettingsMutation) SetPortOverlayIP(i ipconv.IP) { - m.port_overlay_ip = &i - m.addport_overlay_ip = nil -} - -// PortOverlayIP returns the value of the "port_overlay_ip" field in the mutation. -func (m *SettingsMutation) PortOverlayIP() (r ipconv.IP, exists bool) { - v := m.port_overlay_ip - if v == nil { - return - } - return *v, true -} - -// OldPortOverlayIP returns the old "port_overlay_ip" field's value of the Settings entity. -// If the Settings object wasn't provided to the builder, the object is fetched from the database. -// An error is returned if the mutation operation is not UpdateOne, or the database query fails. -func (m *SettingsMutation) OldPortOverlayIP(ctx context.Context) (v ipconv.IP, err error) { - if !m.op.Is(OpUpdateOne) { - return v, errors.New("OldPortOverlayIP is only allowed on UpdateOne operations") - } - if m.id == nil || m.oldValue == nil { - return v, errors.New("OldPortOverlayIP requires an ID field in the mutation") - } - oldValue, err := m.oldValue(ctx) - if err != nil { - return v, fmt.Errorf("querying old value for OldPortOverlayIP: %w", err) - } - return oldValue.PortOverlayIP, nil -} - -// AddPortOverlayIP adds i to the "port_overlay_ip" field. -func (m *SettingsMutation) AddPortOverlayIP(i ipconv.IP) { - if m.addport_overlay_ip != nil { - *m.addport_overlay_ip += i - } else { - m.addport_overlay_ip = &i - } -} - -// AddedPortOverlayIP returns the value that was added to the "port_overlay_ip" field in this mutation. -func (m *SettingsMutation) AddedPortOverlayIP() (r ipconv.IP, exists bool) { - v := m.addport_overlay_ip - if v == nil { - return - } - return *v, true -} - -// ResetPortOverlayIP resets all changes to the "port_overlay_ip" field. -func (m *SettingsMutation) ResetPortOverlayIP() { - m.port_overlay_ip = nil - m.addport_overlay_ip = nil -} - // SetLetsencryptRegistration sets the "letsencrypt_registration" field. func (m *SettingsMutation) SetLetsencryptRegistration(hb helpers.EncryptedBytes) { m.letsencrypt_registration = &hb @@ -1383,13 +2582,16 @@ func (m *SettingsMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *SettingsMutation) Fields() []string { - fields := make([]string, 0, 13) + fields := make([]string, 0, 11) if m.created_time != nil { fields = append(fields, settings.FieldCreatedTime) } if m.updated_time != nil { fields = append(fields, settings.FieldUpdatedTime) } + if m.network_id != nil { + fields = append(fields, settings.FieldNetworkID) + } if m.domain_zone != nil { fields = append(fields, settings.FieldDomainZone) } @@ -1402,18 +2604,9 @@ func (m *SettingsMutation) Fields() []string { if m.ca_key != nil { fields = append(fields, settings.FieldCaKey) } - if m.lighthouse_crt != nil { - fields = append(fields, settings.FieldLighthouseCrt) - } - if m.lighthouse_key != nil { - fields = append(fields, settings.FieldLighthouseKey) - } if m.cidr != nil { fields = append(fields, settings.FieldCidr) } - if m.port_overlay_ip != nil { - fields = append(fields, settings.FieldPortOverlayIP) - } if m.letsencrypt_registration != nil { fields = append(fields, settings.FieldLetsencryptRegistration) } @@ -1435,6 +2628,8 @@ func (m *SettingsMutation) Field(name string) (ent.Value, bool) { return m.CreatedTime() case settings.FieldUpdatedTime: return m.UpdatedTime() + case settings.FieldNetworkID: + return m.NetworkID() case settings.FieldDomainZone: return m.DomainZone() case settings.FieldCipher: @@ -1443,14 +2638,8 @@ func (m *SettingsMutation) Field(name string) (ent.Value, bool) { return m.CaCrt() case settings.FieldCaKey: return m.CaKey() - case settings.FieldLighthouseCrt: - return m.LighthouseCrt() - case settings.FieldLighthouseKey: - return m.LighthouseKey() case settings.FieldCidr: return m.Cidr() - case settings.FieldPortOverlayIP: - return m.PortOverlayIP() case settings.FieldLetsencryptRegistration: return m.LetsencryptRegistration() case settings.FieldTLSCert: @@ -1470,6 +2659,8 @@ func (m *SettingsMutation) OldField(ctx context.Context, name string) (ent.Value return m.OldCreatedTime(ctx) case settings.FieldUpdatedTime: return m.OldUpdatedTime(ctx) + case settings.FieldNetworkID: + return m.OldNetworkID(ctx) case settings.FieldDomainZone: return m.OldDomainZone(ctx) case settings.FieldCipher: @@ -1478,14 +2669,8 @@ func (m *SettingsMutation) OldField(ctx context.Context, name string) (ent.Value return m.OldCaCrt(ctx) case settings.FieldCaKey: return m.OldCaKey(ctx) - case settings.FieldLighthouseCrt: - return m.OldLighthouseCrt(ctx) - case settings.FieldLighthouseKey: - return m.OldLighthouseKey(ctx) case settings.FieldCidr: return m.OldCidr(ctx) - case settings.FieldPortOverlayIP: - return m.OldPortOverlayIP(ctx) case settings.FieldLetsencryptRegistration: return m.OldLetsencryptRegistration(ctx) case settings.FieldTLSCert: @@ -1515,6 +2700,13 @@ func (m *SettingsMutation) SetField(name string, value ent.Value) error { } m.SetUpdatedTime(v) return nil + case settings.FieldNetworkID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetNetworkID(v) + return nil case settings.FieldDomainZone: v, ok := value.(string) if !ok { @@ -1543,20 +2735,6 @@ func (m *SettingsMutation) SetField(name string, value ent.Value) error { } m.SetCaKey(v) return nil - case settings.FieldLighthouseCrt: - v, ok := value.(helpers.EncryptedBytes) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetLighthouseCrt(v) - return nil - case settings.FieldLighthouseKey: - v, ok := value.(helpers.EncryptedBytes) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetLighthouseKey(v) - return nil case settings.FieldCidr: v, ok := value.(helpers.IpCidr) if !ok { @@ -1564,13 +2742,6 @@ func (m *SettingsMutation) SetField(name string, value ent.Value) error { } m.SetCidr(v) return nil - case settings.FieldPortOverlayIP: - v, ok := value.(ipconv.IP) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.SetPortOverlayIP(v) - return nil case settings.FieldLetsencryptRegistration: v, ok := value.(helpers.EncryptedBytes) if !ok { @@ -1599,21 +2770,13 @@ func (m *SettingsMutation) SetField(name string, value ent.Value) error { // AddedFields returns all numeric fields that were incremented/decremented during // this mutation. func (m *SettingsMutation) AddedFields() []string { - var fields []string - if m.addport_overlay_ip != nil { - fields = append(fields, settings.FieldPortOverlayIP) - } - return fields + return nil } // AddedField returns the numeric value that was incremented/decremented on a field // with the given name. The second boolean return value indicates that this field // was not set, or was not defined in the schema. func (m *SettingsMutation) AddedField(name string) (ent.Value, bool) { - switch name { - case settings.FieldPortOverlayIP: - return m.AddedPortOverlayIP() - } return nil, false } @@ -1622,13 +2785,6 @@ func (m *SettingsMutation) AddedField(name string) (ent.Value, bool) { // type. func (m *SettingsMutation) AddField(name string, value ent.Value) error { switch name { - case settings.FieldPortOverlayIP: - v, ok := value.(ipconv.IP) - if !ok { - return fmt.Errorf("unexpected type %T for field %s", value, name) - } - m.AddPortOverlayIP(v) - return nil } return fmt.Errorf("unknown Settings numeric field %s", name) } @@ -1689,6 +2845,9 @@ func (m *SettingsMutation) ResetField(name string) error { case settings.FieldUpdatedTime: m.ResetUpdatedTime() return nil + case settings.FieldNetworkID: + m.ResetNetworkID() + return nil case settings.FieldDomainZone: m.ResetDomainZone() return nil @@ -1701,18 +2860,9 @@ func (m *SettingsMutation) ResetField(name string) error { case settings.FieldCaKey: m.ResetCaKey() return nil - case settings.FieldLighthouseCrt: - m.ResetLighthouseCrt() - return nil - case settings.FieldLighthouseKey: - m.ResetLighthouseKey() - return nil case settings.FieldCidr: m.ResetCidr() return nil - case settings.FieldPortOverlayIP: - m.ResetPortOverlayIP() - return nil case settings.FieldLetsencryptRegistration: m.ResetLetsencryptRegistration() return nil diff --git a/westport/db/ent/predicate/predicate.go b/westport/db/ent/predicate/predicate.go index 56435ce..78d6618 100644 --- a/westport/db/ent/predicate/predicate.go +++ b/westport/db/ent/predicate/predicate.go @@ -9,5 +9,11 @@ import ( // Device is the predicate function for device builders. type Device func(*sql.Selector) +// Host is the predicate function for host builders. +type Host func(*sql.Selector) + +// Lighthouse is the predicate function for lighthouse builders. +type Lighthouse func(*sql.Selector) + // Settings is the predicate function for settings builders. type Settings func(*sql.Selector) diff --git a/westport/db/ent/privacy/privacy.go b/westport/db/ent/privacy/privacy.go index a71a6ac..b1c1ec3 100644 --- a/westport/db/ent/privacy/privacy.go +++ b/westport/db/ent/privacy/privacy.go @@ -135,6 +135,54 @@ func (f DeviceMutationRuleFunc) EvalMutation(ctx context.Context, m ent.Mutation return Denyf("ent/privacy: unexpected mutation type %T, expect *ent.DeviceMutation", m) } +// The HostQueryRuleFunc type is an adapter to allow the use of ordinary +// functions as a query rule. +type HostQueryRuleFunc func(context.Context, *ent.HostQuery) error + +// EvalQuery return f(ctx, q). +func (f HostQueryRuleFunc) EvalQuery(ctx context.Context, q ent.Query) error { + if q, ok := q.(*ent.HostQuery); ok { + return f(ctx, q) + } + return Denyf("ent/privacy: unexpected query type %T, expect *ent.HostQuery", q) +} + +// The HostMutationRuleFunc type is an adapter to allow the use of ordinary +// functions as a mutation rule. +type HostMutationRuleFunc func(context.Context, *ent.HostMutation) error + +// EvalMutation calls f(ctx, m). +func (f HostMutationRuleFunc) EvalMutation(ctx context.Context, m ent.Mutation) error { + if m, ok := m.(*ent.HostMutation); ok { + return f(ctx, m) + } + return Denyf("ent/privacy: unexpected mutation type %T, expect *ent.HostMutation", m) +} + +// The LighthouseQueryRuleFunc type is an adapter to allow the use of ordinary +// functions as a query rule. +type LighthouseQueryRuleFunc func(context.Context, *ent.LighthouseQuery) error + +// EvalQuery return f(ctx, q). +func (f LighthouseQueryRuleFunc) EvalQuery(ctx context.Context, q ent.Query) error { + if q, ok := q.(*ent.LighthouseQuery); ok { + return f(ctx, q) + } + return Denyf("ent/privacy: unexpected query type %T, expect *ent.LighthouseQuery", q) +} + +// The LighthouseMutationRuleFunc type is an adapter to allow the use of ordinary +// functions as a mutation rule. +type LighthouseMutationRuleFunc func(context.Context, *ent.LighthouseMutation) error + +// EvalMutation calls f(ctx, m). +func (f LighthouseMutationRuleFunc) EvalMutation(ctx context.Context, m ent.Mutation) error { + if m, ok := m.(*ent.LighthouseMutation); ok { + return f(ctx, m) + } + return Denyf("ent/privacy: unexpected mutation type %T, expect *ent.LighthouseMutation", m) +} + // The SettingsQueryRuleFunc type is an adapter to allow the use of ordinary // functions as a query rule. type SettingsQueryRuleFunc func(context.Context, *ent.SettingsQuery) error @@ -196,6 +244,10 @@ func queryFilter(q ent.Query) (Filter, error) { switch q := q.(type) { case *ent.DeviceQuery: return q.Filter(), nil + case *ent.HostQuery: + return q.Filter(), nil + case *ent.LighthouseQuery: + return q.Filter(), nil case *ent.SettingsQuery: return q.Filter(), nil default: @@ -207,6 +259,10 @@ func mutationFilter(m ent.Mutation) (Filter, error) { switch m := m.(type) { case *ent.DeviceMutation: return m.Filter(), nil + case *ent.HostMutation: + return m.Filter(), nil + case *ent.LighthouseMutation: + return m.Filter(), nil case *ent.SettingsMutation: return m.Filter(), nil default: diff --git a/westport/db/ent/runtime.go b/westport/db/ent/runtime.go index 6eb50f2..e9a7e83 100644 --- a/westport/db/ent/runtime.go +++ b/westport/db/ent/runtime.go @@ -6,6 +6,8 @@ import ( "time" "github.com/sprisa/west/westport/db/ent/device" + "github.com/sprisa/west/westport/db/ent/host" + "github.com/sprisa/west/westport/db/ent/lighthouse" "github.com/sprisa/west/westport/db/ent/settings" "github.com/sprisa/west/westport/db/schema" ) @@ -37,6 +39,44 @@ func init() { deviceDescIP := deviceFields[1].Descriptor() // device.IPValidator is a validator for the "ip" field. It is called by the builders before save. device.IPValidator = deviceDescIP.Validators[0].(func(uint32) error) + hostMixin := schema.Host{}.Mixin() + hostMixinFields0 := hostMixin[0].Fields() + _ = hostMixinFields0 + hostFields := schema.Host{}.Fields() + _ = hostFields + // hostDescCreatedTime is the schema descriptor for created_time field. + hostDescCreatedTime := hostMixinFields0[0].Descriptor() + // host.DefaultCreatedTime holds the default value on creation for the created_time field. + host.DefaultCreatedTime = hostDescCreatedTime.Default.(func() time.Time) + // hostDescUpdatedTime is the schema descriptor for updated_time field. + hostDescUpdatedTime := hostMixinFields0[1].Descriptor() + // host.DefaultUpdatedTime holds the default value on creation for the updated_time field. + host.DefaultUpdatedTime = hostDescUpdatedTime.Default.(func() time.Time) + // host.UpdateDefaultUpdatedTime holds the default value on update for the updated_time field. + host.UpdateDefaultUpdatedTime = hostDescUpdatedTime.UpdateDefault.(func() time.Time) + // hostDescIP is the schema descriptor for ip field. + hostDescIP := hostFields[0].Descriptor() + // host.IPValidator is a validator for the "ip" field. It is called by the builders before save. + host.IPValidator = hostDescIP.Validators[0].(func(uint32) error) + lighthouseMixin := schema.Lighthouse{}.Mixin() + lighthouseMixinFields0 := lighthouseMixin[0].Fields() + _ = lighthouseMixinFields0 + lighthouseFields := schema.Lighthouse{}.Fields() + _ = lighthouseFields + // lighthouseDescCreatedTime is the schema descriptor for created_time field. + lighthouseDescCreatedTime := lighthouseMixinFields0[0].Descriptor() + // lighthouse.DefaultCreatedTime holds the default value on creation for the created_time field. + lighthouse.DefaultCreatedTime = lighthouseDescCreatedTime.Default.(func() time.Time) + // lighthouseDescUpdatedTime is the schema descriptor for updated_time field. + lighthouseDescUpdatedTime := lighthouseMixinFields0[1].Descriptor() + // lighthouse.DefaultUpdatedTime holds the default value on creation for the updated_time field. + lighthouse.DefaultUpdatedTime = lighthouseDescUpdatedTime.Default.(func() time.Time) + // lighthouse.UpdateDefaultUpdatedTime holds the default value on update for the updated_time field. + lighthouse.UpdateDefaultUpdatedTime = lighthouseDescUpdatedTime.UpdateDefault.(func() time.Time) + // lighthouseDescIP is the schema descriptor for ip field. + lighthouseDescIP := lighthouseFields[0].Descriptor() + // lighthouse.IPValidator is a validator for the "ip" field. It is called by the builders before save. + lighthouse.IPValidator = lighthouseDescIP.Validators[0].(func(uint32) error) settingsMixin := schema.Settings{}.Mixin() settingsMixinFields0 := settingsMixin[0].Fields() _ = settingsMixinFields0 @@ -52,8 +92,12 @@ func init() { settings.DefaultUpdatedTime = settingsDescUpdatedTime.Default.(func() time.Time) // settings.UpdateDefaultUpdatedTime holds the default value on update for the updated_time field. settings.UpdateDefaultUpdatedTime = settingsDescUpdatedTime.UpdateDefault.(func() time.Time) + // settingsDescNetworkID is the schema descriptor for network_id field. + settingsDescNetworkID := settingsFields[0].Descriptor() + // settings.DefaultNetworkID holds the default value on creation for the network_id field. + settings.DefaultNetworkID = settingsDescNetworkID.Default.(string) // settingsDescCipher is the schema descriptor for cipher field. - settingsDescCipher := settingsFields[1].Descriptor() + settingsDescCipher := settingsFields[2].Descriptor() // settings.DefaultCipher holds the default value on creation for the cipher field. settings.DefaultCipher = settingsDescCipher.Default.(string) } diff --git a/westport/db/ent/runtime/runtime.go b/westport/db/ent/runtime/runtime.go index b761bcc..dfedb59 100644 --- a/westport/db/ent/runtime/runtime.go +++ b/westport/db/ent/runtime/runtime.go @@ -5,6 +5,6 @@ package runtime // The schema-stitching logic is generated in github.com/sprisa/west/westport/db/ent/runtime.go const ( - Version = "v0.14.5" // Version of ent codegen. - Sum = "h1:Rj2WOYJtCkWyFo6a+5wB3EfBRP0rnx1fMk6gGA0UUe4=" // Sum of ent codegen. + Version = "v0.14.6" // Version of ent codegen. + Sum = "h1:/f2696BpwuWAEEG6PVGWflg6+Inrpq4pRWuNlWz/Skk=" // Sum of ent codegen. ) diff --git a/westport/db/ent/settings.go b/westport/db/ent/settings.go index 4bfb1d3..50c1b6d 100644 --- a/westport/db/ent/settings.go +++ b/westport/db/ent/settings.go @@ -9,7 +9,6 @@ import ( "entgo.io/ent" "entgo.io/ent/dialect/sql" - "github.com/sprisa/west/util/ipconv" "github.com/sprisa/west/westport/db/ent/settings" "github.com/sprisa/west/westport/db/helpers" ) @@ -23,6 +22,8 @@ type Settings struct { CreatedTime time.Time `json:"created_time,omitempty"` // Time ent was updated UpdatedTime time.Time `json:"updated_time,omitempty"` + // NetworkID holds the value of the "network_id" field. + NetworkID string `json:"network_id,omitempty"` // Domain zone to use for nameserver DomainZone string `json:"domain_zone,omitempty"` // Nebula cipher. aes or chachapoly @@ -31,14 +32,8 @@ type Settings struct { CaCrt helpers.EncryptedBytes `json:"ca_crt,omitempty"` // CaKey holds the value of the "ca_key" field. CaKey helpers.EncryptedBytes `json:"-"` - // LighthouseCrt holds the value of the "lighthouse_crt" field. - LighthouseCrt helpers.EncryptedBytes `json:"-"` - // LighthouseKey holds the value of the "lighthouse_key" field. - LighthouseKey helpers.EncryptedBytes `json:"-"` // Network cidr range Cidr helpers.IpCidr `json:"cidr,omitempty"` - // Network cidr range - PortOverlayIP ipconv.IP `json:"port_overlay_ip,omitempty"` // LetsencryptRegistration holds the value of the "letsencrypt_registration" field. LetsencryptRegistration helpers.EncryptedBytes `json:"-"` // TLSCert holds the value of the "tls_cert" field. @@ -55,13 +50,13 @@ func (*Settings) scanValues(columns []string) ([]any, error) { switch columns[i] { case settings.FieldTLSCert, settings.FieldTLSCertKey: values[i] = &sql.NullScanner{S: new(helpers.EncryptedBytes)} - case settings.FieldCaCrt, settings.FieldCaKey, settings.FieldLighthouseCrt, settings.FieldLighthouseKey, settings.FieldLetsencryptRegistration: + case settings.FieldCaCrt, settings.FieldCaKey, settings.FieldLetsencryptRegistration: values[i] = new(helpers.EncryptedBytes) case settings.FieldCidr: values[i] = new(helpers.IpCidr) - case settings.FieldID, settings.FieldPortOverlayIP: + case settings.FieldID: values[i] = new(sql.NullInt64) - case settings.FieldDomainZone, settings.FieldCipher: + case settings.FieldNetworkID, settings.FieldDomainZone, settings.FieldCipher: values[i] = new(sql.NullString) case settings.FieldCreatedTime, settings.FieldUpdatedTime: values[i] = new(sql.NullTime) @@ -98,6 +93,12 @@ func (_m *Settings) assignValues(columns []string, values []any) error { } else if value.Valid { _m.UpdatedTime = value.Time } + case settings.FieldNetworkID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field network_id", values[i]) + } else if value.Valid { + _m.NetworkID = value.String + } case settings.FieldDomainZone: if value, ok := values[i].(*sql.NullString); !ok { return fmt.Errorf("unexpected type %T for field domain_zone", values[i]) @@ -122,30 +123,12 @@ func (_m *Settings) assignValues(columns []string, values []any) error { } else if value != nil { _m.CaKey = *value } - case settings.FieldLighthouseCrt: - if value, ok := values[i].(*helpers.EncryptedBytes); !ok { - return fmt.Errorf("unexpected type %T for field lighthouse_crt", values[i]) - } else if value != nil { - _m.LighthouseCrt = *value - } - case settings.FieldLighthouseKey: - if value, ok := values[i].(*helpers.EncryptedBytes); !ok { - return fmt.Errorf("unexpected type %T for field lighthouse_key", values[i]) - } else if value != nil { - _m.LighthouseKey = *value - } case settings.FieldCidr: if value, ok := values[i].(*helpers.IpCidr); !ok { return fmt.Errorf("unexpected type %T for field cidr", values[i]) } else if value != nil { _m.Cidr = *value } - case settings.FieldPortOverlayIP: - if value, ok := values[i].(*sql.NullInt64); !ok { - return fmt.Errorf("unexpected type %T for field port_overlay_ip", values[i]) - } else if value.Valid { - _m.PortOverlayIP = ipconv.IP(value.Int64) - } case settings.FieldLetsencryptRegistration: if value, ok := values[i].(*helpers.EncryptedBytes); !ok { return fmt.Errorf("unexpected type %T for field letsencrypt_registration", values[i]) @@ -208,6 +191,9 @@ func (_m *Settings) String() string { builder.WriteString("updated_time=") builder.WriteString(_m.UpdatedTime.Format(time.ANSIC)) builder.WriteString(", ") + builder.WriteString("network_id=") + builder.WriteString(_m.NetworkID) + builder.WriteString(", ") builder.WriteString("domain_zone=") builder.WriteString(_m.DomainZone) builder.WriteString(", ") @@ -219,16 +205,9 @@ func (_m *Settings) String() string { builder.WriteString(", ") builder.WriteString("ca_key=") builder.WriteString(", ") - builder.WriteString("lighthouse_crt=") - builder.WriteString(", ") - builder.WriteString("lighthouse_key=") - builder.WriteString(", ") builder.WriteString("cidr=") builder.WriteString(fmt.Sprintf("%v", _m.Cidr)) builder.WriteString(", ") - builder.WriteString("port_overlay_ip=") - builder.WriteString(fmt.Sprintf("%v", _m.PortOverlayIP)) - builder.WriteString(", ") builder.WriteString("letsencrypt_registration=") builder.WriteString(", ") builder.WriteString("tls_cert=") diff --git a/westport/db/ent/settings/settings.go b/westport/db/ent/settings/settings.go index 2aaeea7..e99d77f 100644 --- a/westport/db/ent/settings/settings.go +++ b/westport/db/ent/settings/settings.go @@ -17,6 +17,8 @@ const ( FieldCreatedTime = "created_time" // FieldUpdatedTime holds the string denoting the updated_time field in the database. FieldUpdatedTime = "updated_time" + // FieldNetworkID holds the string denoting the network_id field in the database. + FieldNetworkID = "network_id" // FieldDomainZone holds the string denoting the domain_zone field in the database. FieldDomainZone = "domain_zone" // FieldCipher holds the string denoting the cipher field in the database. @@ -25,14 +27,8 @@ const ( FieldCaCrt = "ca_crt" // FieldCaKey holds the string denoting the ca_key field in the database. FieldCaKey = "ca_key" - // FieldLighthouseCrt holds the string denoting the lighthouse_crt field in the database. - FieldLighthouseCrt = "lighthouse_crt" - // FieldLighthouseKey holds the string denoting the lighthouse_key field in the database. - FieldLighthouseKey = "lighthouse_key" // FieldCidr holds the string denoting the cidr field in the database. FieldCidr = "cidr" - // FieldPortOverlayIP holds the string denoting the port_overlay_ip field in the database. - FieldPortOverlayIP = "port_overlay_ip" // FieldLetsencryptRegistration holds the string denoting the letsencrypt_registration field in the database. FieldLetsencryptRegistration = "letsencrypt_registration" // FieldTLSCert holds the string denoting the tls_cert field in the database. @@ -48,14 +44,12 @@ var Columns = []string{ FieldID, FieldCreatedTime, FieldUpdatedTime, + FieldNetworkID, FieldDomainZone, FieldCipher, FieldCaCrt, FieldCaKey, - FieldLighthouseCrt, - FieldLighthouseKey, FieldCidr, - FieldPortOverlayIP, FieldLetsencryptRegistration, FieldTLSCert, FieldTLSCertKey, @@ -78,6 +72,8 @@ var ( DefaultUpdatedTime func() time.Time // UpdateDefaultUpdatedTime holds the default value on update for the "updated_time" field. UpdateDefaultUpdatedTime func() time.Time + // DefaultNetworkID holds the default value on creation for the "network_id" field. + DefaultNetworkID string // DefaultCipher holds the default value on creation for the "cipher" field. DefaultCipher string ) @@ -100,6 +96,11 @@ func ByUpdatedTime(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldUpdatedTime, opts...).ToFunc() } +// ByNetworkID orders the results by the network_id field. +func ByNetworkID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldNetworkID, opts...).ToFunc() +} + // ByDomainZone orders the results by the domain_zone field. func ByDomainZone(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldDomainZone, opts...).ToFunc() @@ -114,8 +115,3 @@ func ByCipher(opts ...sql.OrderTermOption) OrderOption { func ByCidr(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldCidr, opts...).ToFunc() } - -// ByPortOverlayIP orders the results by the port_overlay_ip field. -func ByPortOverlayIP(opts ...sql.OrderTermOption) OrderOption { - return sql.OrderByField(FieldPortOverlayIP, opts...).ToFunc() -} diff --git a/westport/db/ent/settings/where.go b/westport/db/ent/settings/where.go index 4ebd38a..5f3ec99 100644 --- a/westport/db/ent/settings/where.go +++ b/westport/db/ent/settings/where.go @@ -6,7 +6,6 @@ import ( "time" "entgo.io/ent/dialect/sql" - "github.com/sprisa/west/util/ipconv" "github.com/sprisa/west/westport/db/ent/predicate" "github.com/sprisa/west/westport/db/helpers" ) @@ -66,6 +65,11 @@ func UpdatedTime(v time.Time) predicate.Settings { return predicate.Settings(sql.FieldEQ(FieldUpdatedTime, v)) } +// NetworkID applies equality check predicate on the "network_id" field. It's identical to NetworkIDEQ. +func NetworkID(v string) predicate.Settings { + return predicate.Settings(sql.FieldEQ(FieldNetworkID, v)) +} + // DomainZone applies equality check predicate on the "domain_zone" field. It's identical to DomainZoneEQ. func DomainZone(v string) predicate.Settings { return predicate.Settings(sql.FieldEQ(FieldDomainZone, v)) @@ -86,27 +90,11 @@ func CaKey(v helpers.EncryptedBytes) predicate.Settings { return predicate.Settings(sql.FieldEQ(FieldCaKey, v)) } -// LighthouseCrt applies equality check predicate on the "lighthouse_crt" field. It's identical to LighthouseCrtEQ. -func LighthouseCrt(v helpers.EncryptedBytes) predicate.Settings { - return predicate.Settings(sql.FieldEQ(FieldLighthouseCrt, v)) -} - -// LighthouseKey applies equality check predicate on the "lighthouse_key" field. It's identical to LighthouseKeyEQ. -func LighthouseKey(v helpers.EncryptedBytes) predicate.Settings { - return predicate.Settings(sql.FieldEQ(FieldLighthouseKey, v)) -} - // Cidr applies equality check predicate on the "cidr" field. It's identical to CidrEQ. func Cidr(v helpers.IpCidr) predicate.Settings { return predicate.Settings(sql.FieldEQ(FieldCidr, v)) } -// PortOverlayIP applies equality check predicate on the "port_overlay_ip" field. It's identical to PortOverlayIPEQ. -func PortOverlayIP(v ipconv.IP) predicate.Settings { - vc := uint32(v) - return predicate.Settings(sql.FieldEQ(FieldPortOverlayIP, vc)) -} - // LetsencryptRegistration applies equality check predicate on the "letsencrypt_registration" field. It's identical to LetsencryptRegistrationEQ. func LetsencryptRegistration(v helpers.EncryptedBytes) predicate.Settings { return predicate.Settings(sql.FieldEQ(FieldLetsencryptRegistration, v)) @@ -202,6 +190,71 @@ func UpdatedTimeLTE(v time.Time) predicate.Settings { return predicate.Settings(sql.FieldLTE(FieldUpdatedTime, v)) } +// NetworkIDEQ applies the EQ predicate on the "network_id" field. +func NetworkIDEQ(v string) predicate.Settings { + return predicate.Settings(sql.FieldEQ(FieldNetworkID, v)) +} + +// NetworkIDNEQ applies the NEQ predicate on the "network_id" field. +func NetworkIDNEQ(v string) predicate.Settings { + return predicate.Settings(sql.FieldNEQ(FieldNetworkID, v)) +} + +// NetworkIDIn applies the In predicate on the "network_id" field. +func NetworkIDIn(vs ...string) predicate.Settings { + return predicate.Settings(sql.FieldIn(FieldNetworkID, vs...)) +} + +// NetworkIDNotIn applies the NotIn predicate on the "network_id" field. +func NetworkIDNotIn(vs ...string) predicate.Settings { + return predicate.Settings(sql.FieldNotIn(FieldNetworkID, vs...)) +} + +// NetworkIDGT applies the GT predicate on the "network_id" field. +func NetworkIDGT(v string) predicate.Settings { + return predicate.Settings(sql.FieldGT(FieldNetworkID, v)) +} + +// NetworkIDGTE applies the GTE predicate on the "network_id" field. +func NetworkIDGTE(v string) predicate.Settings { + return predicate.Settings(sql.FieldGTE(FieldNetworkID, v)) +} + +// NetworkIDLT applies the LT predicate on the "network_id" field. +func NetworkIDLT(v string) predicate.Settings { + return predicate.Settings(sql.FieldLT(FieldNetworkID, v)) +} + +// NetworkIDLTE applies the LTE predicate on the "network_id" field. +func NetworkIDLTE(v string) predicate.Settings { + return predicate.Settings(sql.FieldLTE(FieldNetworkID, v)) +} + +// NetworkIDContains applies the Contains predicate on the "network_id" field. +func NetworkIDContains(v string) predicate.Settings { + return predicate.Settings(sql.FieldContains(FieldNetworkID, v)) +} + +// NetworkIDHasPrefix applies the HasPrefix predicate on the "network_id" field. +func NetworkIDHasPrefix(v string) predicate.Settings { + return predicate.Settings(sql.FieldHasPrefix(FieldNetworkID, v)) +} + +// NetworkIDHasSuffix applies the HasSuffix predicate on the "network_id" field. +func NetworkIDHasSuffix(v string) predicate.Settings { + return predicate.Settings(sql.FieldHasSuffix(FieldNetworkID, v)) +} + +// NetworkIDEqualFold applies the EqualFold predicate on the "network_id" field. +func NetworkIDEqualFold(v string) predicate.Settings { + return predicate.Settings(sql.FieldEqualFold(FieldNetworkID, v)) +} + +// NetworkIDContainsFold applies the ContainsFold predicate on the "network_id" field. +func NetworkIDContainsFold(v string) predicate.Settings { + return predicate.Settings(sql.FieldContainsFold(FieldNetworkID, v)) +} + // DomainZoneEQ applies the EQ predicate on the "domain_zone" field. func DomainZoneEQ(v string) predicate.Settings { return predicate.Settings(sql.FieldEQ(FieldDomainZone, v)) @@ -422,86 +475,6 @@ func CaKeyLTE(v helpers.EncryptedBytes) predicate.Settings { return predicate.Settings(sql.FieldLTE(FieldCaKey, v)) } -// LighthouseCrtEQ applies the EQ predicate on the "lighthouse_crt" field. -func LighthouseCrtEQ(v helpers.EncryptedBytes) predicate.Settings { - return predicate.Settings(sql.FieldEQ(FieldLighthouseCrt, v)) -} - -// LighthouseCrtNEQ applies the NEQ predicate on the "lighthouse_crt" field. -func LighthouseCrtNEQ(v helpers.EncryptedBytes) predicate.Settings { - return predicate.Settings(sql.FieldNEQ(FieldLighthouseCrt, v)) -} - -// LighthouseCrtIn applies the In predicate on the "lighthouse_crt" field. -func LighthouseCrtIn(vs ...helpers.EncryptedBytes) predicate.Settings { - return predicate.Settings(sql.FieldIn(FieldLighthouseCrt, vs...)) -} - -// LighthouseCrtNotIn applies the NotIn predicate on the "lighthouse_crt" field. -func LighthouseCrtNotIn(vs ...helpers.EncryptedBytes) predicate.Settings { - return predicate.Settings(sql.FieldNotIn(FieldLighthouseCrt, vs...)) -} - -// LighthouseCrtGT applies the GT predicate on the "lighthouse_crt" field. -func LighthouseCrtGT(v helpers.EncryptedBytes) predicate.Settings { - return predicate.Settings(sql.FieldGT(FieldLighthouseCrt, v)) -} - -// LighthouseCrtGTE applies the GTE predicate on the "lighthouse_crt" field. -func LighthouseCrtGTE(v helpers.EncryptedBytes) predicate.Settings { - return predicate.Settings(sql.FieldGTE(FieldLighthouseCrt, v)) -} - -// LighthouseCrtLT applies the LT predicate on the "lighthouse_crt" field. -func LighthouseCrtLT(v helpers.EncryptedBytes) predicate.Settings { - return predicate.Settings(sql.FieldLT(FieldLighthouseCrt, v)) -} - -// LighthouseCrtLTE applies the LTE predicate on the "lighthouse_crt" field. -func LighthouseCrtLTE(v helpers.EncryptedBytes) predicate.Settings { - return predicate.Settings(sql.FieldLTE(FieldLighthouseCrt, v)) -} - -// LighthouseKeyEQ applies the EQ predicate on the "lighthouse_key" field. -func LighthouseKeyEQ(v helpers.EncryptedBytes) predicate.Settings { - return predicate.Settings(sql.FieldEQ(FieldLighthouseKey, v)) -} - -// LighthouseKeyNEQ applies the NEQ predicate on the "lighthouse_key" field. -func LighthouseKeyNEQ(v helpers.EncryptedBytes) predicate.Settings { - return predicate.Settings(sql.FieldNEQ(FieldLighthouseKey, v)) -} - -// LighthouseKeyIn applies the In predicate on the "lighthouse_key" field. -func LighthouseKeyIn(vs ...helpers.EncryptedBytes) predicate.Settings { - return predicate.Settings(sql.FieldIn(FieldLighthouseKey, vs...)) -} - -// LighthouseKeyNotIn applies the NotIn predicate on the "lighthouse_key" field. -func LighthouseKeyNotIn(vs ...helpers.EncryptedBytes) predicate.Settings { - return predicate.Settings(sql.FieldNotIn(FieldLighthouseKey, vs...)) -} - -// LighthouseKeyGT applies the GT predicate on the "lighthouse_key" field. -func LighthouseKeyGT(v helpers.EncryptedBytes) predicate.Settings { - return predicate.Settings(sql.FieldGT(FieldLighthouseKey, v)) -} - -// LighthouseKeyGTE applies the GTE predicate on the "lighthouse_key" field. -func LighthouseKeyGTE(v helpers.EncryptedBytes) predicate.Settings { - return predicate.Settings(sql.FieldGTE(FieldLighthouseKey, v)) -} - -// LighthouseKeyLT applies the LT predicate on the "lighthouse_key" field. -func LighthouseKeyLT(v helpers.EncryptedBytes) predicate.Settings { - return predicate.Settings(sql.FieldLT(FieldLighthouseKey, v)) -} - -// LighthouseKeyLTE applies the LTE predicate on the "lighthouse_key" field. -func LighthouseKeyLTE(v helpers.EncryptedBytes) predicate.Settings { - return predicate.Settings(sql.FieldLTE(FieldLighthouseKey, v)) -} - // CidrEQ applies the EQ predicate on the "cidr" field. func CidrEQ(v helpers.IpCidr) predicate.Settings { return predicate.Settings(sql.FieldEQ(FieldCidr, v)) @@ -572,60 +545,6 @@ func CidrContainsFold(v helpers.IpCidr) predicate.Settings { return predicate.Settings(sql.FieldContainsFold(FieldCidr, vc)) } -// PortOverlayIPEQ applies the EQ predicate on the "port_overlay_ip" field. -func PortOverlayIPEQ(v ipconv.IP) predicate.Settings { - vc := uint32(v) - return predicate.Settings(sql.FieldEQ(FieldPortOverlayIP, vc)) -} - -// PortOverlayIPNEQ applies the NEQ predicate on the "port_overlay_ip" field. -func PortOverlayIPNEQ(v ipconv.IP) predicate.Settings { - vc := uint32(v) - return predicate.Settings(sql.FieldNEQ(FieldPortOverlayIP, vc)) -} - -// PortOverlayIPIn applies the In predicate on the "port_overlay_ip" field. -func PortOverlayIPIn(vs ...ipconv.IP) predicate.Settings { - v := make([]any, len(vs)) - for i := range v { - v[i] = uint32(vs[i]) - } - return predicate.Settings(sql.FieldIn(FieldPortOverlayIP, v...)) -} - -// PortOverlayIPNotIn applies the NotIn predicate on the "port_overlay_ip" field. -func PortOverlayIPNotIn(vs ...ipconv.IP) predicate.Settings { - v := make([]any, len(vs)) - for i := range v { - v[i] = uint32(vs[i]) - } - return predicate.Settings(sql.FieldNotIn(FieldPortOverlayIP, v...)) -} - -// PortOverlayIPGT applies the GT predicate on the "port_overlay_ip" field. -func PortOverlayIPGT(v ipconv.IP) predicate.Settings { - vc := uint32(v) - return predicate.Settings(sql.FieldGT(FieldPortOverlayIP, vc)) -} - -// PortOverlayIPGTE applies the GTE predicate on the "port_overlay_ip" field. -func PortOverlayIPGTE(v ipconv.IP) predicate.Settings { - vc := uint32(v) - return predicate.Settings(sql.FieldGTE(FieldPortOverlayIP, vc)) -} - -// PortOverlayIPLT applies the LT predicate on the "port_overlay_ip" field. -func PortOverlayIPLT(v ipconv.IP) predicate.Settings { - vc := uint32(v) - return predicate.Settings(sql.FieldLT(FieldPortOverlayIP, vc)) -} - -// PortOverlayIPLTE applies the LTE predicate on the "port_overlay_ip" field. -func PortOverlayIPLTE(v ipconv.IP) predicate.Settings { - vc := uint32(v) - return predicate.Settings(sql.FieldLTE(FieldPortOverlayIP, vc)) -} - // LetsencryptRegistrationEQ applies the EQ predicate on the "letsencrypt_registration" field. func LetsencryptRegistrationEQ(v helpers.EncryptedBytes) predicate.Settings { return predicate.Settings(sql.FieldEQ(FieldLetsencryptRegistration, v)) diff --git a/westport/db/ent/settings_create.go b/westport/db/ent/settings_create.go index c5b32ff..2b6c0be 100644 --- a/westport/db/ent/settings_create.go +++ b/westport/db/ent/settings_create.go @@ -10,7 +10,6 @@ import ( "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" - "github.com/sprisa/west/util/ipconv" "github.com/sprisa/west/westport/db/ent/settings" "github.com/sprisa/west/westport/db/helpers" ) @@ -50,6 +49,20 @@ func (_c *SettingsCreate) SetNillableUpdatedTime(v *time.Time) *SettingsCreate { return _c } +// SetNetworkID sets the "network_id" field. +func (_c *SettingsCreate) SetNetworkID(v string) *SettingsCreate { + _c.mutation.SetNetworkID(v) + return _c +} + +// SetNillableNetworkID sets the "network_id" field if the given value is not nil. +func (_c *SettingsCreate) SetNillableNetworkID(v *string) *SettingsCreate { + if v != nil { + _c.SetNetworkID(*v) + } + return _c +} + // SetDomainZone sets the "domain_zone" field. func (_c *SettingsCreate) SetDomainZone(v string) *SettingsCreate { _c.mutation.SetDomainZone(v) @@ -90,30 +103,12 @@ func (_c *SettingsCreate) SetCaKey(v helpers.EncryptedBytes) *SettingsCreate { return _c } -// SetLighthouseCrt sets the "lighthouse_crt" field. -func (_c *SettingsCreate) SetLighthouseCrt(v helpers.EncryptedBytes) *SettingsCreate { - _c.mutation.SetLighthouseCrt(v) - return _c -} - -// SetLighthouseKey sets the "lighthouse_key" field. -func (_c *SettingsCreate) SetLighthouseKey(v helpers.EncryptedBytes) *SettingsCreate { - _c.mutation.SetLighthouseKey(v) - return _c -} - // SetCidr sets the "cidr" field. func (_c *SettingsCreate) SetCidr(v helpers.IpCidr) *SettingsCreate { _c.mutation.SetCidr(v) return _c } -// SetPortOverlayIP sets the "port_overlay_ip" field. -func (_c *SettingsCreate) SetPortOverlayIP(v ipconv.IP) *SettingsCreate { - _c.mutation.SetPortOverlayIP(v) - return _c -} - // SetLetsencryptRegistration sets the "letsencrypt_registration" field. func (_c *SettingsCreate) SetLetsencryptRegistration(v helpers.EncryptedBytes) *SettingsCreate { _c.mutation.SetLetsencryptRegistration(v) @@ -175,6 +170,10 @@ func (_c *SettingsCreate) defaults() { v := settings.DefaultUpdatedTime() _c.mutation.SetUpdatedTime(v) } + if _, ok := _c.mutation.NetworkID(); !ok { + v := settings.DefaultNetworkID + _c.mutation.SetNetworkID(v) + } if _, ok := _c.mutation.Cipher(); !ok { v := settings.DefaultCipher _c.mutation.SetCipher(v) @@ -189,6 +188,9 @@ func (_c *SettingsCreate) check() error { if _, ok := _c.mutation.UpdatedTime(); !ok { return &ValidationError{Name: "updated_time", err: errors.New(`ent: missing required field "Settings.updated_time"`)} } + if _, ok := _c.mutation.NetworkID(); !ok { + return &ValidationError{Name: "network_id", err: errors.New(`ent: missing required field "Settings.network_id"`)} + } if _, ok := _c.mutation.Cipher(); !ok { return &ValidationError{Name: "cipher", err: errors.New(`ent: missing required field "Settings.cipher"`)} } @@ -198,18 +200,9 @@ func (_c *SettingsCreate) check() error { if _, ok := _c.mutation.CaKey(); !ok { return &ValidationError{Name: "ca_key", err: errors.New(`ent: missing required field "Settings.ca_key"`)} } - if _, ok := _c.mutation.LighthouseCrt(); !ok { - return &ValidationError{Name: "lighthouse_crt", err: errors.New(`ent: missing required field "Settings.lighthouse_crt"`)} - } - if _, ok := _c.mutation.LighthouseKey(); !ok { - return &ValidationError{Name: "lighthouse_key", err: errors.New(`ent: missing required field "Settings.lighthouse_key"`)} - } if _, ok := _c.mutation.Cidr(); !ok { return &ValidationError{Name: "cidr", err: errors.New(`ent: missing required field "Settings.cidr"`)} } - if _, ok := _c.mutation.PortOverlayIP(); !ok { - return &ValidationError{Name: "port_overlay_ip", err: errors.New(`ent: missing required field "Settings.port_overlay_ip"`)} - } return nil } @@ -244,6 +237,10 @@ func (_c *SettingsCreate) createSpec() (*Settings, *sqlgraph.CreateSpec) { _spec.SetField(settings.FieldUpdatedTime, field.TypeTime, value) _node.UpdatedTime = value } + if value, ok := _c.mutation.NetworkID(); ok { + _spec.SetField(settings.FieldNetworkID, field.TypeString, value) + _node.NetworkID = value + } if value, ok := _c.mutation.DomainZone(); ok { _spec.SetField(settings.FieldDomainZone, field.TypeString, value) _node.DomainZone = value @@ -260,22 +257,10 @@ func (_c *SettingsCreate) createSpec() (*Settings, *sqlgraph.CreateSpec) { _spec.SetField(settings.FieldCaKey, field.TypeBytes, value) _node.CaKey = value } - if value, ok := _c.mutation.LighthouseCrt(); ok { - _spec.SetField(settings.FieldLighthouseCrt, field.TypeBytes, value) - _node.LighthouseCrt = value - } - if value, ok := _c.mutation.LighthouseKey(); ok { - _spec.SetField(settings.FieldLighthouseKey, field.TypeBytes, value) - _node.LighthouseKey = value - } if value, ok := _c.mutation.Cidr(); ok { _spec.SetField(settings.FieldCidr, field.TypeString, value) _node.Cidr = value } - if value, ok := _c.mutation.PortOverlayIP(); ok { - _spec.SetField(settings.FieldPortOverlayIP, field.TypeUint32, value) - _node.PortOverlayIP = value - } if value, ok := _c.mutation.LetsencryptRegistration(); ok { _spec.SetField(settings.FieldLetsencryptRegistration, field.TypeBytes, value) _node.LetsencryptRegistration = value diff --git a/westport/db/ent/settings_update.go b/westport/db/ent/settings_update.go index 7254085..8d31b14 100644 --- a/westport/db/ent/settings_update.go +++ b/westport/db/ent/settings_update.go @@ -11,7 +11,6 @@ import ( "entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/schema/field" - "github.com/sprisa/west/util/ipconv" "github.com/sprisa/west/westport/db/ent/predicate" "github.com/sprisa/west/westport/db/ent/settings" "github.com/sprisa/west/westport/db/helpers" @@ -82,18 +81,6 @@ func (_u *SettingsUpdate) SetCaKey(v helpers.EncryptedBytes) *SettingsUpdate { return _u } -// SetLighthouseCrt sets the "lighthouse_crt" field. -func (_u *SettingsUpdate) SetLighthouseCrt(v helpers.EncryptedBytes) *SettingsUpdate { - _u.mutation.SetLighthouseCrt(v) - return _u -} - -// SetLighthouseKey sets the "lighthouse_key" field. -func (_u *SettingsUpdate) SetLighthouseKey(v helpers.EncryptedBytes) *SettingsUpdate { - _u.mutation.SetLighthouseKey(v) - return _u -} - // SetCidr sets the "cidr" field. func (_u *SettingsUpdate) SetCidr(v helpers.IpCidr) *SettingsUpdate { _u.mutation.SetCidr(v) @@ -108,27 +95,6 @@ func (_u *SettingsUpdate) SetNillableCidr(v *helpers.IpCidr) *SettingsUpdate { return _u } -// SetPortOverlayIP sets the "port_overlay_ip" field. -func (_u *SettingsUpdate) SetPortOverlayIP(v ipconv.IP) *SettingsUpdate { - _u.mutation.ResetPortOverlayIP() - _u.mutation.SetPortOverlayIP(v) - return _u -} - -// SetNillablePortOverlayIP sets the "port_overlay_ip" field if the given value is not nil. -func (_u *SettingsUpdate) SetNillablePortOverlayIP(v *ipconv.IP) *SettingsUpdate { - if v != nil { - _u.SetPortOverlayIP(*v) - } - return _u -} - -// AddPortOverlayIP adds value to the "port_overlay_ip" field. -func (_u *SettingsUpdate) AddPortOverlayIP(v ipconv.IP) *SettingsUpdate { - _u.mutation.AddPortOverlayIP(v) - return _u -} - // SetLetsencryptRegistration sets the "letsencrypt_registration" field. func (_u *SettingsUpdate) SetLetsencryptRegistration(v helpers.EncryptedBytes) *SettingsUpdate { _u.mutation.SetLetsencryptRegistration(v) @@ -233,21 +199,9 @@ func (_u *SettingsUpdate) sqlSave(ctx context.Context) (_node int, err error) { if value, ok := _u.mutation.CaKey(); ok { _spec.SetField(settings.FieldCaKey, field.TypeBytes, value) } - if value, ok := _u.mutation.LighthouseCrt(); ok { - _spec.SetField(settings.FieldLighthouseCrt, field.TypeBytes, value) - } - if value, ok := _u.mutation.LighthouseKey(); ok { - _spec.SetField(settings.FieldLighthouseKey, field.TypeBytes, value) - } if value, ok := _u.mutation.Cidr(); ok { _spec.SetField(settings.FieldCidr, field.TypeString, value) } - if value, ok := _u.mutation.PortOverlayIP(); ok { - _spec.SetField(settings.FieldPortOverlayIP, field.TypeUint32, value) - } - if value, ok := _u.mutation.AddedPortOverlayIP(); ok { - _spec.AddField(settings.FieldPortOverlayIP, field.TypeUint32, value) - } if value, ok := _u.mutation.LetsencryptRegistration(); ok { _spec.SetField(settings.FieldLetsencryptRegistration, field.TypeBytes, value) } @@ -338,18 +292,6 @@ func (_u *SettingsUpdateOne) SetCaKey(v helpers.EncryptedBytes) *SettingsUpdateO return _u } -// SetLighthouseCrt sets the "lighthouse_crt" field. -func (_u *SettingsUpdateOne) SetLighthouseCrt(v helpers.EncryptedBytes) *SettingsUpdateOne { - _u.mutation.SetLighthouseCrt(v) - return _u -} - -// SetLighthouseKey sets the "lighthouse_key" field. -func (_u *SettingsUpdateOne) SetLighthouseKey(v helpers.EncryptedBytes) *SettingsUpdateOne { - _u.mutation.SetLighthouseKey(v) - return _u -} - // SetCidr sets the "cidr" field. func (_u *SettingsUpdateOne) SetCidr(v helpers.IpCidr) *SettingsUpdateOne { _u.mutation.SetCidr(v) @@ -364,27 +306,6 @@ func (_u *SettingsUpdateOne) SetNillableCidr(v *helpers.IpCidr) *SettingsUpdateO return _u } -// SetPortOverlayIP sets the "port_overlay_ip" field. -func (_u *SettingsUpdateOne) SetPortOverlayIP(v ipconv.IP) *SettingsUpdateOne { - _u.mutation.ResetPortOverlayIP() - _u.mutation.SetPortOverlayIP(v) - return _u -} - -// SetNillablePortOverlayIP sets the "port_overlay_ip" field if the given value is not nil. -func (_u *SettingsUpdateOne) SetNillablePortOverlayIP(v *ipconv.IP) *SettingsUpdateOne { - if v != nil { - _u.SetPortOverlayIP(*v) - } - return _u -} - -// AddPortOverlayIP adds value to the "port_overlay_ip" field. -func (_u *SettingsUpdateOne) AddPortOverlayIP(v ipconv.IP) *SettingsUpdateOne { - _u.mutation.AddPortOverlayIP(v) - return _u -} - // SetLetsencryptRegistration sets the "letsencrypt_registration" field. func (_u *SettingsUpdateOne) SetLetsencryptRegistration(v helpers.EncryptedBytes) *SettingsUpdateOne { _u.mutation.SetLetsencryptRegistration(v) @@ -519,21 +440,9 @@ func (_u *SettingsUpdateOne) sqlSave(ctx context.Context) (_node *Settings, err if value, ok := _u.mutation.CaKey(); ok { _spec.SetField(settings.FieldCaKey, field.TypeBytes, value) } - if value, ok := _u.mutation.LighthouseCrt(); ok { - _spec.SetField(settings.FieldLighthouseCrt, field.TypeBytes, value) - } - if value, ok := _u.mutation.LighthouseKey(); ok { - _spec.SetField(settings.FieldLighthouseKey, field.TypeBytes, value) - } if value, ok := _u.mutation.Cidr(); ok { _spec.SetField(settings.FieldCidr, field.TypeString, value) } - if value, ok := _u.mutation.PortOverlayIP(); ok { - _spec.SetField(settings.FieldPortOverlayIP, field.TypeUint32, value) - } - if value, ok := _u.mutation.AddedPortOverlayIP(); ok { - _spec.AddField(settings.FieldPortOverlayIP, field.TypeUint32, value) - } if value, ok := _u.mutation.LetsencryptRegistration(); ok { _spec.SetField(settings.FieldLetsencryptRegistration, field.TypeBytes, value) } diff --git a/westport/db/ent/tx.go b/westport/db/ent/tx.go index 8375700..8271976 100644 --- a/westport/db/ent/tx.go +++ b/westport/db/ent/tx.go @@ -14,6 +14,10 @@ type Tx struct { config // Device is the client for interacting with the Device builders. Device *DeviceClient + // Host is the client for interacting with the Host builders. + Host *HostClient + // Lighthouse is the client for interacting with the Lighthouse builders. + Lighthouse *LighthouseClient // Settings is the client for interacting with the Settings builders. Settings *SettingsClient @@ -148,6 +152,8 @@ func (tx *Tx) Client() *Client { func (tx *Tx) init() { tx.Device = NewDeviceClient(tx.config) + tx.Host = NewHostClient(tx.config) + tx.Lighthouse = NewLighthouseClient(tx.config) tx.Settings = NewSettingsClient(tx.config) } diff --git a/westport/db/helpers/encrypted.go b/westport/db/helpers/encrypted.go index 0ae0fcf..344cd6c 100644 --- a/westport/db/helpers/encrypted.go +++ b/westport/db/helpers/encrypted.go @@ -6,12 +6,18 @@ import ( "encoding/base64" "fmt" + "golang.org/x/crypto/argon2" "golang.org/x/crypto/chacha20poly1305" ) var EncryptionKey [32]byte -// var EncryptionKey = []byte("passphrasewhichneedstobe32bytes!") +var encryptionSalt = []byte("west-port-v1") + +func SetEncryptionPassword(password []byte) { + key := argon2.IDKey(password, encryptionSalt, 3, 64*1024, 4, 32) + copy(EncryptionKey[:], key) +} // EncryptedBytes is a custom type that automatically encrypts/decrypts type EncryptedBytes []byte @@ -37,7 +43,7 @@ func (e *EncryptedBytes) Scan(value any) error { return nil } - decrypted, err := decrypt(string(encrypted)) + decrypted, err := Decrypt(string(encrypted)) if err != nil { return fmt.Errorf("failed to decrypt: %w", err) } @@ -52,7 +58,7 @@ func (e EncryptedBytes) Value() (driver.Value, error) { return []byte{}, nil } - encrypted, err := encrypt(e) + encrypted, err := Encrypt(e) if err != nil { return nil, fmt.Errorf("failed to encrypt: %w", err) } @@ -60,7 +66,7 @@ func (e EncryptedBytes) Value() (driver.Value, error) { return []byte(encrypted), nil } -func encrypt(plaintext []byte) (string, error) { +func Encrypt(plaintext []byte) (string, error) { aead, err := chacha20poly1305.NewX(EncryptionKey[:]) if err != nil { return "", err @@ -77,7 +83,7 @@ func encrypt(plaintext []byte) (string, error) { return base64.StdEncoding.EncodeToString(ciphertext), nil } -func decrypt(ciphertext string) ([]byte, error) { +func Decrypt(ciphertext string) ([]byte, error) { data, err := base64.StdEncoding.DecodeString(ciphertext) if err != nil { return []byte{}, err diff --git a/westport/db/helpers/ip.go b/westport/db/helpers/ip.go index b8f6d4d..a9a8faa 100644 --- a/westport/db/helpers/ip.go +++ b/westport/db/helpers/ip.go @@ -21,9 +21,14 @@ func (s *IpCidr) Scan(value any) error { return nil } - v, ok := value.(string) - if !ok { - return fmt.Errorf("unexpected type for EncryptedString: %T", value) + var v string + switch value := value.(type) { + case string: + v = value + case []byte: + v = string(value) + default: + return fmt.Errorf("unexpected type for IpCidr: %T", value) } prefix, err := netip.ParsePrefix(v) diff --git a/westport/db/helpers/ip_test.go b/westport/db/helpers/ip_test.go new file mode 100644 index 0000000..ec68668 --- /dev/null +++ b/westport/db/helpers/ip_test.go @@ -0,0 +1,22 @@ +package helpers + +import "testing" + +func TestIpCidrScan(t *testing.T) { + for _, value := range []any{"10.10.10.1/24", []byte("10.10.10.1/24")} { + var cidr IpCidr + if err := cidr.Scan(value); err != nil { + t.Fatalf("Scan(%T): %v", value, err) + } + if got := cidr.String(); got != "10.10.10.1/24" { + t.Fatalf("Scan(%T) = %q", value, got) + } + } +} + +func TestIpCidrScanRejectsUnsupportedType(t *testing.T) { + var cidr IpCidr + if err := cidr.Scan(42); err == nil { + t.Fatal("expected unsupported scan type to fail") + } +} diff --git a/westport/db/migrate/main.go b/westport/db/migrate/main.go index 16f67ea..1ce428a 100644 --- a/westport/db/migrate/main.go +++ b/westport/db/migrate/main.go @@ -9,7 +9,7 @@ import ( ) func main() { - err := migrate.Migrate() + err := migrate.Migrate("sqlite") if err != nil { l.Log.Err(err).Msg("error migrating") return diff --git a/westport/db/migrate/migrate.go b/westport/db/migrate/migrate.go index 8e3f7b2..5cf17dd 100644 --- a/westport/db/migrate/migrate.go +++ b/westport/db/migrate/migrate.go @@ -10,9 +10,9 @@ import ( "github.com/sprisa/x/sig" ) -func Migrate() error { +func Migrate(dataSource string) error { ctx := sig.ShutdownContext(context.Background()) - client, err := db.OpenDB() + client, err := db.OpenDB(ctx, dataSource) if err != nil { return err } diff --git a/westport/db/schema/device.go b/westport/db/schema/device.go index 4a7f1af..9058150 100644 --- a/westport/db/schema/device.go +++ b/westport/db/schema/device.go @@ -6,12 +6,14 @@ import ( "entgo.io/contrib/entgql" "entgo.io/ent" "entgo.io/ent/schema" + "entgo.io/ent/schema/edge" "entgo.io/ent/schema/field" "entgo.io/ent/schema/index" "github.com/anandvarma/namegen" "github.com/sprisa/west/util/ipconv" "github.com/sprisa/west/westport/db/helpers" "github.com/sprisa/west/westport/db/mixin" + "github.com/vektah/gqlparser/v2/ast" ) type Device struct { @@ -35,6 +37,15 @@ func (Device) Fields() []ent.Field { field.Uint32("ip"). Immutable(). GoType(ipconv.IP(0)). + Annotations(entgql.Directives(entgql.Directive{ + Name: "goField", + Arguments: ast.ArgumentList{ + &ast.Argument{ + Name: "forceResolver", + Value: &ast.Value{Raw: "true", Kind: ast.BooleanValue}, + }, + }, + })). Validate(func(v uint32) error { ip := ipconv.IP(v) ipv4 := ip.ToIPV4() @@ -59,7 +70,9 @@ func (Device) Fields() []ent.Field { } func (Device) Edges() []ent.Edge { - return []ent.Edge{} + return []ent.Edge{ + edge.To("host", Host.Type).Required().Unique(), + } } func (Device) Indexes() []ent.Index { @@ -68,8 +81,6 @@ func (Device) Indexes() []ent.Index { Unique(), index.Fields("name"). Unique(), - index.Fields("token"). - Unique(), } } diff --git a/westport/db/schema/host.go b/westport/db/schema/host.go new file mode 100644 index 0000000..324d4c7 --- /dev/null +++ b/westport/db/schema/host.go @@ -0,0 +1,41 @@ +package schema + +import ( + "fmt" + + "entgo.io/ent" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/sprisa/west/util/ipconv" + "github.com/sprisa/west/westport/db/mixin" +) + +type Host struct { + ent.Schema +} + +func (Host) Fields() []ent.Field { + return []ent.Field{ + field.Uint32("ip"). + Immutable(). + GoType(ipconv.IP(0)). + Validate(func(v uint32) error { + if ipconv.IP(v).ToIPV4() == nil { + return fmt.Errorf("invalid ipv4 address `%d`", v) + } + return nil + }), + } +} + +func (Host) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("ip").Unique(), + } +} + +func (Host) Mixin() []ent.Mixin { + return []ent.Mixin{ + mixin.TimeMixin{}, + } +} diff --git a/westport/db/schema/lighthouse.go b/westport/db/schema/lighthouse.go new file mode 100644 index 0000000..0ab581b --- /dev/null +++ b/westport/db/schema/lighthouse.go @@ -0,0 +1,56 @@ +package schema + +import ( + "fmt" + + "entgo.io/ent" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/sprisa/west/util/ipconv" + "github.com/sprisa/west/westport/db/helpers" + "github.com/sprisa/west/westport/db/mixin" +) + +type Lighthouse struct { + ent.Schema +} + +func (Lighthouse) Fields() []ent.Field { + return []ent.Field{ + field.Uint32("ip"). + Immutable(). + GoType(ipconv.IP(0)). + Validate(func(v uint32) error { + if ipconv.IP(v).ToIPV4() == nil { + return fmt.Errorf("invalid ipv4 address `%d`", v) + } + return nil + }). + Comment("Nebula overlay IPv4 of the lighthouse"), + field.String("endpoint"). + Comment("Public Nebula host and UDP port"), + field.Bytes("certificate"). + Sensitive(). + GoType(helpers.EncryptedBytes{}), + field.Bytes("key"). + Sensitive(). + GoType(helpers.EncryptedBytes{}), + field.String("api_endpoint"). + Comment("Physical API host:port for this node"), + } +} + +func (Lighthouse) Edges() []ent.Edge { + return []ent.Edge{ + edge.To("host", Host.Type).Required().Unique(), + } +} + +func (Lighthouse) Indexes() []ent.Index { + return []ent.Index{index.Fields("ip").Unique()} +} + +func (Lighthouse) Mixin() []ent.Mixin { + return []ent.Mixin{mixin.TimeMixin{}} +} diff --git a/westport/db/schema/settings.go b/westport/db/schema/settings.go index 925abbf..fbf7c7d 100644 --- a/westport/db/schema/settings.go +++ b/westport/db/schema/settings.go @@ -6,7 +6,7 @@ import ( "entgo.io/ent" "entgo.io/ent/schema" "entgo.io/ent/schema/field" - "github.com/sprisa/west/util/ipconv" + "entgo.io/ent/schema/index" "github.com/sprisa/west/westport/db/helpers" "github.com/sprisa/west/westport/db/mixin" ) @@ -17,6 +17,9 @@ type Settings struct { func (Settings) Fields() []ent.Field { return []ent.Field{ + field.String("network_id"). + Default("default"). + Immutable(), field.String("domain_zone"). Optional(). Comment("Domain zone to use for nameserver"), @@ -28,18 +31,9 @@ func (Settings) Fields() []ent.Field { field.Bytes("ca_key"). Sensitive(). GoType(helpers.EncryptedBytes{}), - field.Bytes("lighthouse_crt"). - Sensitive(). - GoType(helpers.EncryptedBytes{}), - field.Bytes("lighthouse_key"). - Sensitive(). - GoType(helpers.EncryptedBytes{}), field.String("cidr"). GoType(helpers.IpCidr{}). Comment("Network cidr range"), - field.Uint32("port_overlay_ip"). - GoType(ipconv.IP(0)). - Comment("Network cidr range"), field.Bytes("letsencrypt_registration"). Sensitive(). GoType(helpers.EncryptedBytes{}). @@ -73,7 +67,9 @@ func (Settings) Edges() []ent.Edge { } func (Settings) Indexes() []ent.Index { - return []ent.Index{} + return []ent.Index{ + index.Fields("network_id").Unique(), + } } func (Settings) Annotations() []schema.Annotation { diff --git a/westport/gql/ent.graphql b/westport/gql/ent.graphql index 9774e4d..e1c0ba9 100644 --- a/westport/gql/ent.graphql +++ b/westport/gql/ent.graphql @@ -22,7 +22,7 @@ type Device implements Node { """ Overlay IPv4 of host """ - ip: Int! + ip: Int! @goField(forceResolver: true) } """ An object with an ID. @@ -89,7 +89,3 @@ type Query { ids: [ID!]! ): [Node]! } -""" -The builtin Time type -""" -scalar Time diff --git a/westport/gql/ent.resolvers.go b/westport/gql/ent.resolvers.go index e221a89..2876e04 100644 --- a/westport/gql/ent.resolvers.go +++ b/westport/gql/ent.resolvers.go @@ -1,8 +1,9 @@ package gql -// This file will be automatically regenerated based on the schema, any resolver implementations +// This file will be automatically regenerated based on the schema, any resolver +// implementations // will be copied through when generating and any unknown code will be moved to the end. -// Code generated by github.com/99designs/gqlgen version v0.17.81 +// Code generated by github.com/99designs/gqlgen version v0.17.86 import ( "context" diff --git a/westport/gql/generated.go b/westport/gql/generated.go index acb700b..1e83a08 100644 --- a/westport/gql/generated.go +++ b/westport/gql/generated.go @@ -58,6 +58,11 @@ type ComplexityRoot struct { UpdatedTime func(childComplexity int) int } + LighthouseConfig struct { + Endpoint func(childComplexity int) int + OverlayIP func(childComplexity int) int + } + Mutation struct { ProvisionDevice func(childComplexity int, input ProvisionDeviceInput) int } @@ -74,6 +79,7 @@ type ComplexityRoot struct { Ca func(childComplexity int) int Cert func(childComplexity int) int Key func(childComplexity int) int + Lighthouses func(childComplexity int) int Name func(childComplexity int) int NetworkCipher func(childComplexity int) int } @@ -145,6 +151,19 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin return e.complexity.Device.UpdatedTime(childComplexity), true + case "LighthouseConfig.endpoint": + if e.complexity.LighthouseConfig.Endpoint == nil { + break + } + + return e.complexity.LighthouseConfig.Endpoint(childComplexity), true + case "LighthouseConfig.overlayIp": + if e.complexity.LighthouseConfig.OverlayIP == nil { + break + } + + return e.complexity.LighthouseConfig.OverlayIP(childComplexity), true + case "Mutation.provision_device": if e.complexity.Mutation.ProvisionDevice == nil { break @@ -206,6 +225,12 @@ func (e *executableSchema) Complexity(ctx context.Context, typeName, field strin } return e.complexity.ProvisionDeviceResponse.Key(childComplexity), true + case "ProvisionDeviceResponse.lighthouses": + if e.complexity.ProvisionDeviceResponse.Lighthouses == nil { + break + } + + return e.complexity.ProvisionDeviceResponse.Lighthouses(childComplexity), true case "ProvisionDeviceResponse.name": if e.complexity.ProvisionDeviceResponse.Name == nil { break @@ -609,6 +634,64 @@ func (ec *executionContext) fieldContext_Device_ip(_ context.Context, field grap return fc, nil } +func (ec *executionContext) _LighthouseConfig_overlayIp(ctx context.Context, field graphql.CollectedField, obj *LighthouseConfig) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_LighthouseConfig_overlayIp, + func(ctx context.Context) (any, error) { + return obj.OverlayIP, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_LighthouseConfig_overlayIp(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "LighthouseConfig", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + +func (ec *executionContext) _LighthouseConfig_endpoint(ctx context.Context, field graphql.CollectedField, obj *LighthouseConfig) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_LighthouseConfig_endpoint, + func(ctx context.Context) (any, error) { + return obj.Endpoint, nil + }, + nil, + ec.marshalNString2string, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_LighthouseConfig_endpoint(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "LighthouseConfig", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + return nil, errors.New("field of type String does not have child fields") + }, + } + return fc, nil +} + func (ec *executionContext) _Mutation_provision_device(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -646,6 +729,8 @@ func (ec *executionContext) fieldContext_Mutation_provision_device(ctx context.C return ec.fieldContext_ProvisionDeviceResponse_access_token(ctx, field) case "networkCipher": return ec.fieldContext_ProvisionDeviceResponse_networkCipher(ctx, field) + case "lighthouses": + return ec.fieldContext_ProvisionDeviceResponse_lighthouses(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type ProvisionDeviceResponse", field.Name) }, @@ -954,6 +1039,41 @@ func (ec *executionContext) fieldContext_ProvisionDeviceResponse_networkCipher(_ return fc, nil } +func (ec *executionContext) _ProvisionDeviceResponse_lighthouses(ctx context.Context, field graphql.CollectedField, obj *ProvisionDeviceResponse) (ret graphql.Marshaler) { + return graphql.ResolveField( + ctx, + ec.OperationContext, + field, + ec.fieldContext_ProvisionDeviceResponse_lighthouses, + func(ctx context.Context) (any, error) { + return obj.Lighthouses, nil + }, + nil, + ec.marshalNLighthouseConfig2ᚕᚖgithubᚗcomᚋsprisaᚋwestᚋwestportᚋgqlᚐLighthouseConfigᚄ, + true, + true, + ) +} + +func (ec *executionContext) fieldContext_ProvisionDeviceResponse_lighthouses(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "ProvisionDeviceResponse", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "overlayIp": + return ec.fieldContext_LighthouseConfig_overlayIp(ctx, field) + case "endpoint": + return ec.fieldContext_LighthouseConfig_endpoint(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type LighthouseConfig", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _Query_node(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { return graphql.ResolveField( ctx, @@ -2631,7 +2751,11 @@ func (ec *executionContext) _Node(ctx context.Context, sel ast.SelectionSet, obj } return ec._Device(ctx, sel, obj) default: - panic(fmt.Errorf("unexpected type %T", obj)) + if typedObj, ok := obj.(graphql.Marshaler); ok { + return typedObj + } else { + panic(fmt.Errorf("unexpected type %T; non-generated variants of Node must implement graphql.Marshaler", obj)) + } } } @@ -2729,6 +2853,50 @@ func (ec *executionContext) _Device(ctx context.Context, sel ast.SelectionSet, o return out } +var lighthouseConfigImplementors = []string{"LighthouseConfig"} + +func (ec *executionContext) _LighthouseConfig(ctx context.Context, sel ast.SelectionSet, obj *LighthouseConfig) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, lighthouseConfigImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("LighthouseConfig") + case "overlayIp": + out.Values[i] = ec._LighthouseConfig_overlayIp(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "endpoint": + out.Values[i] = ec._LighthouseConfig_endpoint(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var mutationImplementors = []string{"Mutation"} func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) graphql.Marshaler { @@ -2867,6 +3035,11 @@ func (ec *executionContext) _ProvisionDeviceResponse(ctx context.Context, sel as if out.Values[i] == graphql.Null { out.Invalids++ } + case "lighthouses": + out.Values[i] = ec._ProvisionDeviceResponse_lighthouses(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } default: panic("unknown field " + strconv.Quote(field.Name)) } @@ -3326,7 +3499,7 @@ func (ec *executionContext) marshalNBoolean2bool(ctx context.Context, sel ast.Se res := graphql.MarshalBoolean(v) if res == graphql.Null { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") } } return res @@ -3342,7 +3515,7 @@ func (ec *executionContext) marshalNID2int(ctx context.Context, sel ast.Selectio res := graphql.MarshalIntID(v) if res == graphql.Null { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") } } return res @@ -3388,12 +3561,66 @@ func (ec *executionContext) marshalNInt2int(ctx context.Context, sel ast.Selecti res := graphql.MarshalInt(v) if res == graphql.Null { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") } } return res } +func (ec *executionContext) marshalNLighthouseConfig2ᚕᚖgithubᚗcomᚋsprisaᚋwestᚋwestportᚋgqlᚐLighthouseConfigᚄ(ctx context.Context, sel ast.SelectionSet, v []*LighthouseConfig) graphql.Marshaler { + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNLighthouseConfig2ᚖgithubᚗcomᚋsprisaᚋwestᚋwestportᚋgqlᚐLighthouseConfig(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + + for _, e := range ret { + if e == graphql.Null { + return graphql.Null + } + } + + return ret +} + +func (ec *executionContext) marshalNLighthouseConfig2ᚖgithubᚗcomᚋsprisaᚋwestᚋwestportᚋgqlᚐLighthouseConfig(ctx context.Context, sel ast.SelectionSet, v *LighthouseConfig) graphql.Marshaler { + if v == nil { + if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") + } + return graphql.Null + } + return ec._LighthouseConfig(ctx, sel, v) +} + func (ec *executionContext) marshalNNode2ᚕgithubᚗcomᚋsprisaᚋwestᚋwestportᚋdbᚋentᚐNoder(ctx context.Context, sel ast.SelectionSet, v []ent.Noder) graphql.Marshaler { ret := make(graphql.Array, len(v)) var wg sync.WaitGroup @@ -3444,7 +3671,7 @@ func (ec *executionContext) marshalNProvisionDeviceResponse2githubᚗcomᚋspris func (ec *executionContext) marshalNProvisionDeviceResponse2ᚖgithubᚗcomᚋsprisaᚋwestᚋwestportᚋgqlᚐProvisionDeviceResponse(ctx context.Context, sel ast.SelectionSet, v *ProvisionDeviceResponse) graphql.Marshaler { if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") } return graphql.Null } @@ -3461,7 +3688,7 @@ func (ec *executionContext) marshalNString2string(ctx context.Context, sel ast.S res := graphql.MarshalString(v) if res == graphql.Null { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") } } return res @@ -3477,7 +3704,7 @@ func (ec *executionContext) marshalNTime2timeᚐTime(ctx context.Context, sel as res := graphql.MarshalTime(v) if res == graphql.Null { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") } } return res @@ -3541,7 +3768,7 @@ func (ec *executionContext) marshalN__DirectiveLocation2string(ctx context.Conte res := graphql.MarshalString(v) if res == graphql.Null { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") } } return res @@ -3713,7 +3940,7 @@ func (ec *executionContext) marshalN__Type2ᚕgithubᚗcomᚋ99designsᚋgqlgen func (ec *executionContext) marshalN__Type2ᚖgithubᚗcomᚋ99designsᚋgqlgenᚋgraphqlᚋintrospectionᚐType(ctx context.Context, sel ast.SelectionSet, v *introspection.Type) graphql.Marshaler { if v == nil { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") } return graphql.Null } @@ -3730,7 +3957,7 @@ func (ec *executionContext) marshalN__TypeKind2string(ctx context.Context, sel a res := graphql.MarshalString(v) if res == graphql.Null { if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) { - ec.Errorf(ctx, "the requested element is null which the schema does not allow") + graphql.AddErrorf(ctx, "the requested element is null which the schema does not allow") } } return res diff --git a/westport/gql/models_gen.go b/westport/gql/models_gen.go index 0266b81..f80dff6 100644 --- a/westport/gql/models_gen.go +++ b/westport/gql/models_gen.go @@ -2,15 +2,21 @@ package gql +type LighthouseConfig struct { + OverlayIP string `json:"overlayIp"` + Endpoint string `json:"endpoint"` +} + type ProvisionDeviceInput struct { Token string `json:"token"` } type ProvisionDeviceResponse struct { - Name string `json:"name"` - Ca string `json:"ca"` - Cert string `json:"cert"` - Key string `json:"key"` - AccessToken string `json:"access_token"` - NetworkCipher string `json:"networkCipher"` + Name string `json:"name"` + Ca string `json:"ca"` + Cert string `json:"cert"` + Key string `json:"key"` + AccessToken string `json:"access_token"` + NetworkCipher string `json:"networkCipher"` + Lighthouses []*LighthouseConfig `json:"lighthouses"` } diff --git a/westport/gql/provision.graphql b/westport/gql/provision.graphql index 4b3116e..4dd4f8a 100644 --- a/westport/gql/provision.graphql +++ b/westport/gql/provision.graphql @@ -1,7 +1,14 @@ +scalar Time + input ProvisionDeviceInput { token: String! } +type LighthouseConfig { + overlayIp: String! + endpoint: String! +} + type ProvisionDeviceResponse { name: String! ca: String! @@ -9,6 +16,7 @@ type ProvisionDeviceResponse { key: String! access_token: String! networkCipher: String! + lighthouses: [LighthouseConfig!]! } diff --git a/westport/gql/provision.resolvers.go b/westport/gql/provision.resolvers.go index 45b6e83..8125b7a 100644 --- a/westport/gql/provision.resolvers.go +++ b/westport/gql/provision.resolvers.go @@ -1,8 +1,9 @@ package gql -// This file will be automatically regenerated based on the schema, any resolver implementations +// This file will be automatically regenerated based on the schema, any resolver +// implementations // will be copied through when generating and any unknown code will be moved to the end. -// Code generated by github.com/99designs/gqlgen version v0.17.81 +// Code generated by github.com/99designs/gqlgen version v0.17.86 import ( "context" @@ -15,6 +16,7 @@ import ( "github.com/sprisa/west/util/auth" "github.com/sprisa/west/util/pki" "github.com/sprisa/west/westport/db/ent" + "github.com/sprisa/west/westport/db/ent/lighthouse" "github.com/sprisa/west/westport/db/helpers" "github.com/sprisa/x/errutil" l "github.com/sprisa/x/log" @@ -82,6 +84,20 @@ func (r *mutationResolver) ProvisionDevice(ctx context.Context, input ProvisionD if err != nil { return nil, errutil.WrapErr(err, "error signing cert") } + ports, err := r.client.Lighthouse.Query().Order(ent.Asc(lighthouse.FieldIP)).All(ctx) + if err != nil { + return nil, errutil.WrapErr(err, "error loading lighthouses") + } + if len(ports) == 0 { + return nil, errors.New("no lighthouses are installed") + } + lighthouses := make([]*LighthouseConfig, 0, len(ports)) + for _, westPort := range ports { + lighthouses = append(lighthouses, &LighthouseConfig{ + OverlayIP: westPort.IP.ToIPV4().String(), + Endpoint: westPort.Endpoint, + }) + } res := &ProvisionDeviceResponse{ Name: dvc.Name, @@ -90,6 +106,7 @@ func (r *mutationResolver) ProvisionDevice(ctx context.Context, input ProvisionD Key: string(cert.Key), AccessToken: "todo", NetworkCipher: settings.Cipher, + Lighthouses: lighthouses, } return res, nil } diff --git a/westport/install.go b/westport/install.go index b252884..8c0e74e 100644 --- a/westport/install.go +++ b/westport/install.go @@ -3,16 +3,17 @@ package westport import ( "context" "errors" + "fmt" "os" "strings" "github.com/sprisa/west/util/ipconv" - "github.com/sprisa/west/util/pki" "github.com/sprisa/west/westport/acme" "github.com/sprisa/west/westport/db" "github.com/sprisa/west/westport/db/ent" "github.com/sprisa/west/westport/db/helpers" "github.com/sprisa/west/westport/db/migrate" + "github.com/sprisa/west/westport/localconfig" "github.com/sprisa/x/errutil" l "github.com/sprisa/x/log" "github.com/urfave/cli/v3" @@ -21,8 +22,21 @@ import ( var InstallCommand = &cli.Command{ Name: "install", Usage: "Install west port", - UsageText: "west port install", + UsageText: "west port install --datastore ", Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "datastore", + Usage: "Datastore connection: sqlite, sqlite://, postgres://, or mysql://", + Required: true, + }, + &cli.StringFlag{ + Name: "add-lighthouse-ip", + Usage: "Add this lighthouse overlay IP to an existing external datastore", + }, + &cli.StringFlag{ + Name: "port-endpoint", + Usage: "Public Nebula endpoint for this lighthouse (defaults to the detected public IP on port 4242)", + }, &cli.StringFlag{ Name: "ca-crt", Value: "ca.crt", @@ -36,7 +50,7 @@ var InstallCommand = &cli.Command{ &cli.StringFlag{ Name: "cidr", Value: "10.10.10.1/24", - Usage: "Network IP cidr range", + Usage: "Network IP cidr range; its address is the first lighthouse IP", }, &cli.StringFlag{ Name: "domain-zone", @@ -52,57 +66,82 @@ var InstallCommand = &cli.Command{ }, }, Action: func(ctx context.Context, c *cli.Command) error { - caPath := c.String("ca-crt") - caKeyPath := c.String("ca-key") - ca, err := os.ReadFile(caPath) - if err != nil { - return errutil.WrapErr(err, "error reading ca at `%s`", caPath) + if err := readEncryptionPassword(); err != nil { + return err } - caKey, err := os.ReadFile(caKeyPath) + + exists, err := localconfig.Exists() if err != nil { - return errutil.WrapErr(err, "error reading ca-key at `%s`", caPath) - } - cidr := c.String("cidr") - domainZone := strings.ToLower(c.String("domain-zone")) - letsencryptEmail := c.String("letsencrypt-email") - letsencryptTOSAccepted := c.Bool("letsencrypt-accept-tos") - if letsencryptEmail != "" && letsencryptTOSAccepted == false { - return errors.New("Required to accept Let's Encrypt terms of service (--letsencrypt-accept-tos)") + return errutil.WrapErr(err, "check local west-port installation") } - if letsencryptEmail != "" && domainZone == "" { - return errors.New("Domain zone must be specified in order to use Let's Encrypt certificates (--domain-zone)") + if exists { + cfg, err := localconfig.Load() + if err != nil { + return fmt.Errorf("config at %s is unreadable: %w; remove it to reinstall", localconfig.FilePath, err) + } + l.Log.Info().Str("datastore", cfg.Datastore).Str("lighthouse", cfg.LighthouseIP). + Msg("West port is already installed on this node") + return nil } - client, err := db.OpenDB() + dataSource := c.String("datastore") + client, err := db.OpenDB(ctx, dataSource) if err != nil { return errutil.WrapErr(err, "error opening db") } defer client.Close() - err = migrate.MigrateClient(ctx, client) - if err != nil { + if err := migrate.MigrateClient(ctx, client); err != nil { return errutil.WrapErr(err, "error migrating db") } - _, err = client.Settings.Query().First(ctx) - if ent.IsNotFound(err) == false { - return errors.New("west port already installed with database present.") + settings, err := client.Settings.Query().Only(ctx) + if err != nil && !ent.IsNotFound(err) { + return errutil.WrapErr(err, "error reading west-port settings") + } + addLighthouse, err := validateInstallRequest( + err == nil, + dataSource, + c.String("add-lighthouse-ip"), + ) + if err != nil { + return err + } + if addLighthouse { + return installAdditionalLighthouse(ctx, c, client, settings, dataSource) } - lhCert, err := pki.SignCert(&pki.SignCertOptions{ - CaCrt: ca, - CaKey: caKey, - Name: "west-port-1", - Ip: cidr, - }) + caPath := c.String("ca-crt") + caKeyPath := c.String("ca-key") + ca, err := os.ReadFile(caPath) if err != nil { - return errutil.WrapErr(err, "error generating west-port cert") + return errutil.WrapErr(err, "error reading ca at `%s`", caPath) + } + caKey, err := os.ReadFile(caKeyPath) + if err != nil { + return errutil.WrapErr(err, "error reading ca-key at `%s`", caKeyPath) + } + cidr := c.String("cidr") + domainZone := strings.ToLower(c.String("domain-zone")) + letsencryptEmail := c.String("letsencrypt-email") + if letsencryptEmail != "" && !c.Bool("letsencrypt-accept-tos") { + return errors.New("required to accept Let's Encrypt terms of service (--letsencrypt-accept-tos)") + } + if letsencryptEmail != "" && domainZone == "" { + return errors.New("domain zone must be specified to use Let's Encrypt certificates (--domain-zone)") } ipCidr, err := helpers.NewIpCidr(cidr) if err != nil { return errutil.WrapErr(err, "error parsing cidr") } - overlayIp, err := ipconv.FromIPAddr(ipCidr.Addr()) + if !ipCidr.Addr().Is4() { + return errors.New("west currently requires an IPv4 network cidr") + } + endpoint, err := resolvePortEndpoint(c.String("port-endpoint")) + if err != nil { + return err + } + lighthouseCert, err := signLighthouseCertificate(ca, caKey, ipCidr.Addr(), ipCidr.Bits()) if err != nil { return err } @@ -113,42 +152,61 @@ var InstallCommand = &cli.Command{ if err != nil { return errutil.WrapErr(err, "error creating new lets encrypt user") } - acmeRegistration, err = acmeUser.ToBytes() if err != nil { return errutil.WrapErr(err, "error serializing acme registration") } - - l.Log.Info(). - Str("email", letsencryptEmail). - Msg("Registered with Let's Encrypt") + l.Log.Info().Str("email", letsencryptEmail).Msg("Registered with Let's Encrypt") } - l.Log.Info().Msg("Create a encryption a password") - err = readEncryptionPassword() + hasHTTPS := len(acmeRegistration) > 0 + apiEndpoint, err := resolveApiEndpoint(endpoint, hasHTTPS) if err != nil { return err } - - err = client.Settings.Create(). + lighthouseIP, err := ipconv.FromIPAddr(ipCidr.Addr()) + if err != nil { + return err + } + tx, err := client.Tx(ctx) + if err != nil { + return err + } + defer tx.Rollback() + if err := tx.Settings.Create(). SetCaCrt(ca). SetCaKey(caKey). - // TODO: Store info in a device so it get's all the - // DNS and uniqueness built in. - SetLighthouseCrt(lhCert.Cert). - SetLighthouseKey(lhCert.Key). SetCidr(ipCidr). - SetPortOverlayIP(overlayIp). SetDomainZone(domainZone). SetLetsencryptRegistration(acmeRegistration). - Exec(ctx) - if err != nil { + Exec(ctx); err != nil { + if ent.IsConstraintError(err) { + return errors.New("datastore is already initialized; use --add-lighthouse-ip on a new node") + } return errutil.WrapErr(err, "error saving settings") } + host, err := tx.Host.Create().SetIP(lighthouseIP).Save(ctx) + if err != nil { + return errutil.WrapErr(err, "reserve overlay IP") + } + if err := tx.Lighthouse.Create(). + SetIP(lighthouseIP). + SetEndpoint(endpoint). + SetAPIEndpoint(apiEndpoint). + SetCertificate(lighthouseCert.Cert). + SetKey(lighthouseCert.Key). + SetHostID(host.ID). + Exec(ctx); err != nil { + return errutil.WrapErr(err, "error saving first lighthouse") + } + if err := commitLocalInstallation(tx, localconfig.Config{ + Datastore: dataSource, + LighthouseIP: ipCidr.Addr().String(), + }); err != nil { + return err + } l.Log.Info().Msg("Done! Use `west port start` to run") - // TODO: Show extra steps on snap mode - // sudo snap connect west:network-control return nil }, } diff --git a/westport/install_lighthouse.go b/westport/install_lighthouse.go new file mode 100644 index 0000000..c9f75c5 --- /dev/null +++ b/westport/install_lighthouse.go @@ -0,0 +1,160 @@ +package westport + +import ( + "context" + "errors" + "fmt" + "net" + "net/netip" + "strconv" + "strings" + + "github.com/sprisa/west/util/info" + "github.com/sprisa/west/util/ipconv" + "github.com/sprisa/west/util/pki" + "github.com/sprisa/west/westport/db" + "github.com/sprisa/west/westport/db/ent" + "github.com/sprisa/west/westport/localconfig" + "github.com/sprisa/x/errutil" + "github.com/urfave/cli/v3" +) + +func validateInstallRequest(initialized bool, dataSource, lighthouseIP string) (bool, error) { + if !initialized { + if lighthouseIP != "" { + return false, errors.New("--add-lighthouse-ip cannot be used before the datastore is initialized") + } + return false, nil + } + if lighthouseIP == "" { + return false, errors.New("datastore is already initialized; --add-lighthouse-ip is required on a new west-port node") + } + typeName, err := db.DatastoreType(dataSource) + if err != nil { + return false, err + } + if typeName == db.DatastoreSQLite { + return false, errors.New("--add-lighthouse-ip requires a PostgreSQL or MySQL datastore") + } + return true, nil +} + +func installAdditionalLighthouse( + ctx context.Context, + c *cli.Command, + client *ent.Client, + settings *ent.Settings, + dataSource string, +) error { + lighthouseIP, err := netip.ParseAddr(c.String("add-lighthouse-ip")) + if err != nil || !lighthouseIP.Is4() { + return fmt.Errorf("invalid --add-lighthouse-ip %q", c.String("add-lighthouse-ip")) + } + if !settings.Cidr.Contains(lighthouseIP) { + return fmt.Errorf("lighthouse IP `%s` must be within network cidr `%s`", lighthouseIP, settings.Cidr) + } + lighthouseIPValue, err := ipconv.FromIPAddr(lighthouseIP) + if err != nil { + return err + } + endpoint, err := resolvePortEndpoint(c.String("port-endpoint")) + if err != nil { + return err + } + hasHTTPS := settings.DomainZone != "" && len(settings.LetsencryptRegistration) > 0 + apiEndpoint, err := resolveApiEndpoint(endpoint, hasHTTPS) + if err != nil { + return err + } + certificate, err := signLighthouseCertificate( + settings.CaCrt, + settings.CaKey, + lighthouseIP, + settings.Cidr.Bits(), + ) + if err != nil { + return err + } + + tx, err := client.Tx(ctx) + if err != nil { + return err + } + defer tx.Rollback() + host, err := tx.Host.Create().SetIP(lighthouseIPValue).Save(ctx) + if err != nil { + if ent.IsConstraintError(err) { + return fmt.Errorf("overlay IP %s is already in use", lighthouseIP) + } + return errutil.WrapErr(err, "reserve overlay IP") + } + if err := tx.Lighthouse.Create(). + SetIP(lighthouseIPValue). + SetEndpoint(endpoint). + SetAPIEndpoint(apiEndpoint). + SetCertificate(certificate.Cert). + SetKey(certificate.Key). + SetHostID(host.ID). + Exec(ctx); err != nil { + return errutil.WrapErr(err, "error saving lighthouse") + } + return commitLocalInstallation(tx, localconfig.Config{ + Datastore: dataSource, + LighthouseIP: lighthouseIP.String(), + }) +} + +func commitLocalInstallation(tx *ent.Tx, cfg localconfig.Config) error { + if err := tx.Commit(); err != nil { + return err + } + return localconfig.Save(cfg) +} + +func resolvePortEndpoint(endpoint string) (string, error) { + if endpoint == "" { + publicIP, err := info.GetPublicIP() + if err != nil { + return "", errutil.WrapErr(err, "detect public IP") + } + if publicIP == nil { + return "", errors.New("public IP service returned an invalid address") + } + return net.JoinHostPort(publicIP.String(), "4242"), nil + } + host, portValue, err := net.SplitHostPort(endpoint) + if err != nil || host == "" { + return "", fmt.Errorf("invalid --port-endpoint %q; expected host:port", endpoint) + } + portNumber, err := strconv.ParseUint(portValue, 10, 16) + if err != nil || portNumber == 0 { + return "", fmt.Errorf("invalid --port-endpoint port %q", portValue) + } + return net.JoinHostPort(host, portValue), nil +} + +func resolveApiEndpoint(nebulaEndpoint string, hasHTTPS bool) (string, error) { + host, _, err := net.SplitHostPort(nebulaEndpoint) + if err != nil { + return "", fmt.Errorf("parse nebula endpoint for API address: %w", err) + } + port := "80" + if hasHTTPS { + port = "443" + } + return net.JoinHostPort(host, port), nil +} + +func signLighthouseCertificate(ca, caKey []byte, ip netip.Addr, bits int) (*pki.SignCertData, error) { + name := "west-port-" + strings.ReplaceAll(ip.String(), ".", "-") + certificate, err := pki.SignCert(&pki.SignCertOptions{ + CaCrt: ca, + CaKey: caKey, + Name: name, + Ip: netip.PrefixFrom(ip, bits).String(), + }) + if err != nil { + return nil, errutil.WrapErr(err, "error generating west-port certificate") + } + return certificate, nil +} diff --git a/westport/install_lighthouse_test.go b/westport/install_lighthouse_test.go new file mode 100644 index 0000000..595f956 --- /dev/null +++ b/westport/install_lighthouse_test.go @@ -0,0 +1,49 @@ +package westport + +import "testing" + +func TestValidateInstallRequest(t *testing.T) { + tests := []struct { + name string + initialized bool + datastore string + ip string + wantAdd bool + wantError bool + }{ + {name: "first install", datastore: "sqlite"}, + {name: "first install cannot add", datastore: "postgres://db/west", ip: "10.10.10.2", wantError: true}, + {name: "existing requires IP", initialized: true, datastore: "postgres://db/west", wantError: true}, + {name: "SQLite cannot add", initialized: true, datastore: "sqlite", ip: "10.10.10.2", wantError: true}, + {name: "Postgres adds", initialized: true, datastore: "postgres://db/west", ip: "10.10.10.2", wantAdd: true}, + {name: "MySQL adds", initialized: true, datastore: "mysql://db/west", ip: "10.10.10.2", wantAdd: true}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got, err := validateInstallRequest(test.initialized, test.datastore, test.ip) + if (err != nil) != test.wantError { + t.Fatalf("error = %v, wantError %v", err, test.wantError) + } + if got != test.wantAdd { + t.Fatalf("add = %v, want %v", got, test.wantAdd) + } + }) + } +} + +func TestResolvePortEndpoint(t *testing.T) { + for _, endpoint := range []string{"203.0.113.10:4242", "lh2.example.com:4242"} { + got, err := resolvePortEndpoint(endpoint) + if err != nil { + t.Fatalf("resolvePortEndpoint(%q): %v", endpoint, err) + } + if got != endpoint { + t.Fatalf("resolvePortEndpoint(%q) = %q", endpoint, got) + } + } + for _, endpoint := range []string{"example.com", "example.com:0", ":4242"} { + if _, err := resolvePortEndpoint(endpoint); err == nil { + t.Fatalf("expected %q to fail", endpoint) + } + } +} diff --git a/westport/localconfig/config.go b/westport/localconfig/config.go new file mode 100644 index 0000000..fa14c0e --- /dev/null +++ b/westport/localconfig/config.go @@ -0,0 +1,85 @@ +package localconfig + +import ( + "encoding/json" + "errors" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/sprisa/west/westport/db/helpers" +) + +var FilePath = "west-port.config.json" + +type Config struct { + Datastore string `json:"datastore"` + LighthouseIP string `json:"lighthouse_ip"` +} + +func Exists() (bool, error) { + _, err := os.Stat(FilePath) + if err == nil { + return true, nil + } + if errors.Is(err, os.ErrNotExist) { + return false, nil + } + return false, err +} + +func Load() (*Config, error) { + ciphertext, err := os.ReadFile(FilePath) + if err != nil { + return nil, err + } + plaintext, err := helpers.Decrypt(strings.TrimSpace(string(ciphertext))) + if err != nil { + return nil, fmt.Errorf("decrypt west-port config: %w", err) + } + var cfg Config + if err := json.Unmarshal(plaintext, &cfg); err != nil { + return nil, fmt.Errorf("decode west-port config: %w", err) + } + if cfg.Datastore == "" || cfg.LighthouseIP == "" { + return nil, errors.New("west-port config is missing datastore or lighthouse_ip") + } + return &cfg, nil +} + +func Save(cfg Config) error { + plaintext, err := json.Marshal(cfg) + if err != nil { + return err + } + ciphertext, err := helpers.Encrypt(plaintext) + if err != nil { + return err + } + dir := filepath.Dir(FilePath) + tmp, err := os.CreateTemp(dir, ".west-port.config.*.tmp") + if err != nil { + return err + } + tmpName := tmp.Name() + if _, err := tmp.WriteString(ciphertext); err != nil { + tmp.Close() + os.Remove(tmpName) + return err + } + if err := tmp.Sync(); err != nil { + tmp.Close() + os.Remove(tmpName) + return err + } + if err := tmp.Close(); err != nil { + os.Remove(tmpName) + return err + } + if err := os.Chmod(tmpName, 0600); err != nil { + os.Remove(tmpName) + return err + } + return os.Rename(tmpName, FilePath) +} diff --git a/westport/localconfig/config_test.go b/westport/localconfig/config_test.go new file mode 100644 index 0000000..4f90682 --- /dev/null +++ b/westport/localconfig/config_test.go @@ -0,0 +1,61 @@ +package localconfig + +import ( + "os" + "path/filepath" + "strings" + "testing" + + "github.com/sprisa/west/westport/db/helpers" +) + +func TestSaveLoadAndRejectDuplicate(t *testing.T) { + originalPath := FilePath + FilePath = filepath.Join(t.TempDir(), "west-port.config.json") + t.Cleanup(func() { FilePath = originalPath }) + helpers.SetEncryptionPassword([]byte("correct horse battery staple")) + + want := Config{Datastore: "postgres://west@example/west", LighthouseIP: "10.10.10.1"} + if err := Save(want); err != nil { + t.Fatal(err) + } + contents, err := os.ReadFile(FilePath) + if err != nil { + t.Fatal(err) + } + if strings.Contains(string(contents), want.Datastore) || strings.HasPrefix(string(contents), "{") { + t.Fatal("config file contains plaintext JSON") + } + info, err := os.Stat(FilePath) + if err != nil { + t.Fatal(err) + } + if info.Mode().Perm() != 0600 { + t.Fatalf("config permissions = %o", info.Mode().Perm()) + } + + got, err := Load() + if err != nil { + t.Fatal(err) + } + if *got != want { + t.Fatalf("Load() = %#v, want %#v", *got, want) + } + if err := Save(want); err != nil { + t.Fatalf("atomic overwrite should succeed: %v", err) + } +} + +func TestLoadRejectsWrongPassword(t *testing.T) { + originalPath := FilePath + FilePath = filepath.Join(t.TempDir(), "west-port.config.json") + t.Cleanup(func() { FilePath = originalPath }) + helpers.SetEncryptionPassword([]byte("correct")) + if err := Save(Config{Datastore: "sqlite", LighthouseIP: "10.10.10.1"}); err != nil { + t.Fatal(err) + } + helpers.SetEncryptionPassword([]byte("wrong")) + if _, err := Load(); err == nil { + t.Fatal("expected wrong password to fail") + } +} diff --git a/westport/shared.go b/westport/shared.go index 277a730..6f89466 100644 --- a/westport/shared.go +++ b/westport/shared.go @@ -28,7 +28,7 @@ func readEncryptionPassword() (err error) { } } - copy(helpers.EncryptionKey[:], pswd) + helpers.SetEncryptionPassword([]byte(pswd)) // l.Log.Info().Msg(pswd) // l.Log.Info().Msgf("key: %s", string(helpers.EncryptionKey[:])) return nil diff --git a/westport/start.go b/westport/start.go index fc2bcc7..ae81252 100644 --- a/westport/start.go +++ b/westport/start.go @@ -6,6 +6,7 @@ import ( "errors" "net" "net/http" + "net/netip" "time" "entgo.io/contrib/entgql" @@ -16,13 +17,16 @@ import ( "github.com/99designs/gqlgen/graphql/handler/transport" "github.com/sprisa/west" "github.com/sprisa/west/config" + "github.com/sprisa/west/util/ipconv" "github.com/sprisa/west/util/merge" "github.com/sprisa/west/westport/acme" "github.com/sprisa/west/westport/db" "github.com/sprisa/west/westport/db/ent" + "github.com/sprisa/west/westport/db/ent/lighthouse" "github.com/sprisa/west/westport/db/migrate" "github.com/sprisa/west/westport/dns" "github.com/sprisa/west/westport/gql" + "github.com/sprisa/west/westport/localconfig" "github.com/sprisa/x/errutil" l "github.com/sprisa/x/log" "github.com/urfave/cli/v3" @@ -58,7 +62,11 @@ func startWestPort(ctx context.Context, c *cli.Command) error { return err } - client, err := db.OpenDB() + localCfg, err := localconfig.Load() + if err != nil { + return errutil.WrapErr(err, "load local west-port config") + } + client, err := db.OpenDB(ctx, localCfg.Datastore) if err != nil { return errutil.WrapErr(err, "error opening db") } @@ -75,6 +83,18 @@ func startWestPort(ctx context.Context, c *cli.Command) error { } return errutil.WrapErr(err, "error initializing settings") } + portAddr, err := netip.ParseAddr(localCfg.LighthouseIP) + if err != nil { + return errutil.WrapErr(err, "parse configured port IP") + } + portIP, err := ipconv.FromIPAddr(portAddr) + if err != nil { + return err + } + westPort, err := client.Lighthouse.Query().Where(lighthouse.IP(portIP)).Only(ctx) + if err != nil { + return errutil.WrapErr(err, "load configured west port") + } l.Log.Debug().Msgf("settings: %+v", settings) @@ -93,7 +113,7 @@ func startWestPort(ctx context.Context, c *cli.Command) error { ) server := &http.Server{Addr: ":80", Handler: mux} var httpsServer *http.Server - if settings.DomainZone != "" { + if settings.DomainZone != "" && len(settings.LetsencryptRegistration) > 0 { httpsServer = &http.Server{ Addr: ":443", Handler: mux, @@ -143,10 +163,10 @@ func startWestPort(ctx context.Context, c *cli.Command) error { l.Log.Info().Msg("Shutting down gql server") ctx, cancel := context.WithTimeout(context.Background(), time.Second*15) defer cancel() - err := errors.Join( - server.Shutdown(ctx), - httpsServer.Shutdown(ctx), - ) + err := server.Shutdown(ctx) + if httpsServer != nil { + err = errors.Join(err, httpsServer.Shutdown(ctx)) + } if err != nil && errors.Is(err, http.ErrServerClosed) == false { l.Log.Err(err).Msg("gql server shutdown") } @@ -161,7 +181,7 @@ func startWestPort(ctx context.Context, c *cli.Command) error { if disableTun { return errors.New("private dns cannot be used with tun disabled") } - addr = net.JoinHostPort(settings.PortOverlayIP.ToIpAddr().String(), "53") + addr = net.JoinHostPort(westPort.IP.ToIpAddr().String(), "53") } return dns.StartCompassDNSServer(ctx, addr, client, settings, dnsProvider) }) @@ -177,8 +197,8 @@ func startWestPort(ctx context.Context, c *cli.Command) error { cfg := &config.Config{ Pki: config.Pki{ Ca: string(settings.CaCrt), - Cert: string(settings.LighthouseCrt), - Key: string(settings.LighthouseKey), + Cert: string(westPort.Certificate), + Key: string(westPort.Key), }, Lighthouse: config.Lighthouse{ AmLighthouse: true,