diff --git a/admin/db-zone.go b/admin/db-zone.go
new file mode 100644
index 0000000..aec0037
--- /dev/null
+++ b/admin/db-zone.go
@@ -0,0 +1,125 @@
+// Copyright or © or Copr. happyDNS (2020)
+//
+// contact@happydns.org
+//
+// This software is a computer program whose purpose is to provide a modern
+// interface to interact with DNS systems.
+//
+// This software is governed by the CeCILL license under French law and abiding
+// by the rules of distribution of free software. You can use, modify and/or
+// redistribute the software under the terms of the CeCILL license as
+// circulated by CEA, CNRS and INRIA at the following URL
+// "http://www.cecill.info".
+//
+// As a counterpart to the access to the source code and rights to copy, modify
+// and redistribute granted by the license, users are provided only with a
+// limited warranty and the software's author, the holder of the economic
+// rights, and the successive licensors have only limited liability.
+//
+// In this respect, the user's attention is drawn to the risks associated with
+// loading, using, modifying and/or developing or reproducing the software by
+// the user in light of its specific status of free software, that may mean
+// that it is complicated to manipulate, and that also therefore means that it
+// is reserved for developers and experienced professionals having in-depth
+// computer knowledge. Users are therefore encouraged to load and test the
+// software's suitability as regards their requirements in conditions enabling
+// the security of their systems and/or data to be ensured and, more generally,
+// to use and operate it in the same conditions as regards security.
+//
+// The fact that you are presently reading this means that you have had
+// knowledge of the CeCILL license and that you accept its terms.
+
+package admin
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "strconv"
+
+ "github.com/julienschmidt/httprouter"
+
+ "git.happydns.org/happydns/api"
+ "git.happydns.org/happydns/config"
+ "git.happydns.org/happydns/model"
+ "git.happydns.org/happydns/storage"
+)
+
+func init() {
+ router.GET("/api/users/:userid/domains/:domain/zones", api.ApiHandler(userHandler(domainHandler(getUserDomainZones))))
+ router.PUT("/api/users/:userid/domains/:domain/zones", api.ApiHandler(userHandler(domainHandler(updateUserDomainZones))))
+ router.POST("/api/users/:userid/domains/:domain/zones", api.ApiHandler(userHandler(domainHandler(newUserDomainZone))))
+
+ router.GET("/api/users/:userid/domains/:domain/zones/:zoneid", api.ApiHandler(zoneHandler(getZone)))
+ router.PUT("/api/users/:userid/domains/:domain/zones/:zoneid", api.ApiHandler(zoneHandler(updateZone)))
+ router.DELETE("/api/users/:userid/domains/:domain/zones/:zoneid", api.ApiHandler(deleteZone))
+
+ router.GET("/api/zones/:zoneid", api.ApiHandler(zoneHandler(getZone)))
+ router.PUT("/api/zones/:zoneid", api.ApiHandler(zoneHandler(updateZone)))
+ router.DELETE("/api/zones/:zoneid", api.ApiHandler(deleteZone))
+}
+
+func getUserDomainZones(_ *config.Options, domain *happydns.Domain, _ httprouter.Params, _ io.Reader) api.Response {
+ return api.NewAPIResponse(domain.ZoneHistory, nil)
+}
+
+func updateUserDomainZones(_ *config.Options, domain *happydns.Domain, _ httprouter.Params, body io.Reader) api.Response {
+ err := json.NewDecoder(body).Decode(&domain.ZoneHistory)
+ if err != nil {
+ return api.NewAPIErrorResponse(http.StatusBadRequest, fmt.Errorf("Something is wrong in received data: %w", err))
+ }
+
+ return api.NewAPIResponse(domain, storage.MainStore.UpdateDomain(domain))
+}
+
+func newUserDomainZone(_ *config.Options, domain *happydns.Domain, _ httprouter.Params, body io.Reader) api.Response {
+ uz := &happydns.Zone{}
+ err := json.NewDecoder(body).Decode(&uz)
+ if err != nil {
+ return api.NewAPIErrorResponse(http.StatusBadRequest, fmt.Errorf("Something is wrong in received data: %w", err))
+ }
+ uz.Id = 0
+
+ return api.NewAPIResponse(uz, storage.MainStore.CreateZone(uz))
+}
+
+func zoneHandler(f func(*config.Options, *happydns.Zone, httprouter.Params, io.Reader) api.Response) func(*config.Options, httprouter.Params, io.Reader) api.Response {
+ return func(opts *config.Options, ps httprouter.Params, body io.Reader) api.Response {
+ zoneid, err := strconv.ParseInt(ps.ByName("zoneid"), 10, 64)
+ if err != nil {
+ return api.NewAPIErrorResponse(http.StatusNotFound, err)
+ } else {
+ zone, err := storage.MainStore.GetZone(zoneid)
+ if err != nil {
+ return api.NewAPIErrorResponse(http.StatusNotFound, err)
+ } else {
+ return f(opts, zone, ps, body)
+ }
+ }
+ }
+}
+
+func getZone(_ *config.Options, zone *happydns.Zone, _ httprouter.Params, _ io.Reader) api.Response {
+ return api.NewAPIResponse(zone, nil)
+}
+
+func updateZone(_ *config.Options, zone *happydns.Zone, _ httprouter.Params, body io.Reader) api.Response {
+ uz := &happydns.Zone{}
+ err := json.NewDecoder(body).Decode(&uz)
+ if err != nil {
+ return api.NewAPIErrorResponse(http.StatusBadRequest, fmt.Errorf("Something is wrong in received data: %w", err))
+ }
+ uz.Id = zone.Id
+
+ return api.NewAPIResponse(uz, storage.MainStore.UpdateZone(uz))
+}
+
+func deleteZone(opts *config.Options, ps httprouter.Params, body io.Reader) api.Response {
+ zoneid, err := strconv.ParseInt(ps.ByName("zoneid"), 10, 64)
+ if err != nil {
+ return api.NewAPIErrorResponse(http.StatusNotFound, err)
+ } else {
+ return api.NewAPIResponse(true, storage.MainStore.DeleteZone(&happydns.Zone{Id: zoneid}))
+ }
+}
diff --git a/api/zones.go b/api/zones.go
new file mode 100644
index 0000000..969aeea
--- /dev/null
+++ b/api/zones.go
@@ -0,0 +1,148 @@
+// Copyright or © or Copr. happyDNS (2020)
+//
+// contact@happydns.org
+//
+// This software is a computer program whose purpose is to provide a modern
+// interface to interact with DNS systems.
+//
+// This software is governed by the CeCILL license under French law and abiding
+// by the rules of distribution of free software. You can use, modify and/or
+// redistribute the software under the terms of the CeCILL license as
+// circulated by CEA, CNRS and INRIA at the following URL
+// "http://www.cecill.info".
+//
+// As a counterpart to the access to the source code and rights to copy, modify
+// and redistribute granted by the license, users are provided only with a
+// limited warranty and the software's author, the holder of the economic
+// rights, and the successive licensors have only limited liability.
+//
+// In this respect, the user's attention is drawn to the risks associated with
+// loading, using, modifying and/or developing or reproducing the software by
+// the user in light of its specific status of free software, that may mean
+// that it is complicated to manipulate, and that also therefore means that it
+// is reserved for developers and experienced professionals having in-depth
+// computer knowledge. Users are therefore encouraged to load and test the
+// software's suitability as regards their requirements in conditions enabling
+// the security of their systems and/or data to be ensured and, more generally,
+// to use and operate it in the same conditions as regards security.
+//
+// The fact that you are presently reading this means that you have had
+// knowledge of the CeCILL license and that you accept its terms.
+
+package api
+
+import (
+ "errors"
+ "io"
+ "net/http"
+ "strconv"
+
+ "github.com/julienschmidt/httprouter"
+
+ "git.happydns.org/happydns/config"
+ "git.happydns.org/happydns/model"
+ "git.happydns.org/happydns/services"
+ "git.happydns.org/happydns/storage"
+)
+
+func init() {
+ router.GET("/api/domains/:domain/zone/:zoneid", apiAuthHandler(zoneHandler(getZone)))
+
+ router.POST("/api/domains/:domain/import_zone", apiAuthHandler(domainHandler(importZone)))
+}
+
+func zoneHandler(f func(*config.Options, *happydns.Domain, *happydns.Zone, io.Reader) Response) func(*config.Options, *happydns.User, httprouter.Params, io.Reader) Response {
+ return func(opts *config.Options, u *happydns.User, ps httprouter.Params, body io.Reader) Response {
+ zoneid, err := strconv.ParseInt(ps.ByName("zoneid"), 10, 64)
+ if err != nil {
+ return APIErrorResponse{
+ err: err,
+ }
+ }
+
+ return domainHandler(func(opts *config.Options, domain *happydns.Domain, body io.Reader) Response {
+ // Check that the zoneid exists in the domain history
+ found := false
+ for _, v := range domain.ZoneHistory {
+ if v.Id == zoneid {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return APIErrorResponse{
+ status: http.StatusNotFound,
+ err: errors.New("Zone not found"),
+ }
+ }
+
+ if zone, err := storage.MainStore.GetZone(zoneid); err != nil {
+ return APIErrorResponse{
+ status: http.StatusNotFound,
+ err: errors.New("Zone not found"),
+ }
+ } else {
+ return f(opts, domain, zone, body)
+ }
+ })(opts, u, ps, body)
+ }
+}
+
+func getZone(opts *config.Options, domain *happydns.Domain, zone *happydns.Zone, body io.Reader) Response {
+ return APIResponse{
+ response: zone,
+ }
+}
+
+func importZone(opts *config.Options, domain *happydns.Domain, body io.Reader) Response {
+ source, err := storage.MainStore.GetSource(&happydns.User{Id: domain.IdUser}, domain.IdSource)
+ if err != nil {
+ return APIErrorResponse{
+ err: err,
+ }
+ }
+
+ zone, err := source.ImportZone(domain)
+ if err != nil {
+ return APIErrorResponse{
+ err: err,
+ }
+ }
+
+ services, aliases, defaultTTL, err := svcs.AnalyzeZone(domain.DomainName, zone)
+ if err != nil {
+ return APIErrorResponse{
+ err: err,
+ }
+ }
+
+ myZone := &happydns.Zone{
+ IdAuthor: domain.IdUser,
+ DefaultTTL: defaultTTL,
+ Aliases: aliases,
+ Services: services,
+ }
+
+ err = storage.MainStore.CreateZone(myZone)
+ if err != nil {
+ return APIErrorResponse{
+ err: err,
+ }
+ }
+
+ domain.ZoneHistory = append(
+ []happydns.ZoneMeta{
+ happydns.ZoneMeta{myZone.Id},
+ }, domain.ZoneHistory...)
+
+ err = storage.MainStore.UpdateDomain(domain)
+ if err != nil {
+ return APIErrorResponse{
+ err: err,
+ }
+ }
+
+ return APIResponse{
+ response: myZone.Id,
+ }
+}
diff --git a/htdocs/src/components/hSubdomainList.vue b/htdocs/src/components/hSubdomainList.vue
index 87c6de5..829959d 100644
--- a/htdocs/src/components/hSubdomainList.vue
+++ b/htdocs/src/components/hSubdomainList.vue
@@ -51,6 +51,10 @@ export default {
domain: {
type: Object,
required: true
+ },
+ zoneMeta: {
+ type: Object,
+ required: true
}
},
@@ -67,7 +71,7 @@ export default {
},
sortedDomains () {
- if (this.myServices === null) {
+ if (this.myServices == null) {
return []
}
@@ -93,13 +97,16 @@ export default {
watch: {
domain: function () {
- this.pullDomain()
+ this.pullZone()
+ },
+ zoneMeta: function () {
+ this.pullZone()
}
},
created () {
- if (this.domain !== undefined) {
- this.pullDomain()
+ if (this.domain !== undefined && this.domain.domain !== undefined && this.zoneMeta !== undefined) {
+ this.pullZone()
}
},
@@ -113,12 +120,9 @@ export default {
}
},
- pullDomain () {
- if (this.domain === undefined || this.domain.domain === undefined) {
- return
- }
+ pullZone () {
axios
- .post('/api/domains/' + encodeURIComponent(this.domain.domain) + '/analyze')
+ .get('/api/domains/' + encodeURIComponent(this.domain.domain) + '/zone/' + encodeURIComponent(this.zoneMeta.id))
.then(
(response) => {
this.myServices = response.data
diff --git a/htdocs/src/views/domain-services.vue b/htdocs/src/views/domain-services.vue
index 246158d..4ce5f28 100644
--- a/htdocs/src/views/domain-services.vue
+++ b/htdocs/src/views/domain-services.vue
@@ -32,10 +32,12 @@
-->
-
+
diff --git a/model/domain.go b/model/domain.go
index b734aec..3345b04 100644
--- a/model/domain.go
+++ b/model/domain.go
@@ -36,10 +36,11 @@ import (
)
type Domain struct {
- Id int64 `json:"id"`
- IdUser int64 `json:"id_owner"`
- IdSource int64 `json:"id_source"`
- DomainName string `json:"domain"`
+ Id int64 `json:"id"`
+ IdUser int64 `json:"id_owner"`
+ IdSource int64 `json:"id_source"`
+ DomainName string `json:"domain"`
+ ZoneHistory []ZoneMeta `json:"zone_history"`
}
type Domains []*Domain
diff --git a/model/service.go b/model/service.go
index 6797a6a..d2f3fd0 100644
--- a/model/service.go
+++ b/model/service.go
@@ -59,3 +59,9 @@ type ServiceCombined struct {
Service
ServiceType
}
+
+var UnmarshalServiceJSON func(*ServiceCombined, []byte) error
+
+func (svc *ServiceCombined) UnmarshalJSON(b []byte) error {
+ return UnmarshalServiceJSON(svc, b)
+}
diff --git a/model/zone.go b/model/zone.go
new file mode 100644
index 0000000..80786ce
--- /dev/null
+++ b/model/zone.go
@@ -0,0 +1,52 @@
+// Copyright or © or Copr. happyDNS (2020)
+//
+// contact@happydns.org
+//
+// This software is a computer program whose purpose is to provide a modern
+// interface to interact with DNS systems.
+//
+// This software is governed by the CeCILL license under French law and abiding
+// by the rules of distribution of free software. You can use, modify and/or
+// redistribute the software under the terms of the CeCILL license as
+// circulated by CEA, CNRS and INRIA at the following URL
+// "http://www.cecill.info".
+//
+// As a counterpart to the access to the source code and rights to copy, modify
+// and redistribute granted by the license, users are provided only with a
+// limited warranty and the software's author, the holder of the economic
+// rights, and the successive licensors have only limited liability.
+//
+// In this respect, the user's attention is drawn to the risks associated with
+// loading, using, modifying and/or developing or reproducing the software by
+// the user in light of its specific status of free software, that may mean
+// that it is complicated to manipulate, and that also therefore means that it
+// is reserved for developers and experienced professionals having in-depth
+// computer knowledge. Users are therefore encouraged to load and test the
+// software's suitability as regards their requirements in conditions enabling
+// the security of their systems and/or data to be ensured and, more generally,
+// to use and operate it in the same conditions as regards security.
+//
+// The fact that you are presently reading this means that you have had
+// knowledge of the CeCILL license and that you accept its terms.
+
+package happydns
+
+import (
+ "time"
+)
+
+type ZoneMeta struct {
+ Id int64 `json:"id"`
+}
+
+type Zone struct {
+ Id int64 `json:"id"`
+ IdAuthor int64 `json:"id_author"`
+ DefaultTTL uint32 `json:"default_ttl"`
+ LastModified *time.Time `json:"last_modified,omitempty"`
+ CommitMsg *string `json:"commit_message,omitempty"`
+ CommitDate *time.Time `json:"commit_date,omitempty"`
+ Published *time.Time `json:"published,omitempty"`
+ Aliases map[string][]string `json:"aliases"`
+ Services map[string][]*ServiceCombined `json:"services"`
+}
diff --git a/services/interfaces.go b/services/interfaces.go
index f2bcb26..e997847 100644
--- a/services/interfaces.go
+++ b/services/interfaces.go
@@ -31,7 +31,11 @@
package svcs
-import ()
+import (
+ "encoding/json"
+
+ "git.happydns.org/happydns/model"
+)
type ServiceInfos struct {
Name string `json:"name"`
@@ -39,3 +43,33 @@ type ServiceInfos struct {
Categories []string `json:"categories"`
Tabs bool `json:"tabs,omitempty"`
}
+
+type serviceCombined struct {
+ Service happydns.Service
+}
+
+func UnmarshalServiceJSON(svc *happydns.ServiceCombined, b []byte) (err error) {
+ var svcType happydns.ServiceType
+ err = json.Unmarshal(b, &svcType)
+ if err != nil {
+ return
+ }
+
+ var tsvc happydns.Service
+ tsvc, err = FindService(svcType.Type)
+
+ mySvc := &serviceCombined{
+ tsvc,
+ }
+
+ err = json.Unmarshal(b, mySvc)
+
+ svc.Service = tsvc
+ svc.ServiceType = svcType
+
+ return
+}
+
+func init() {
+ happydns.UnmarshalServiceJSON = UnmarshalServiceJSON
+}
diff --git a/storage/interface.go b/storage/interface.go
index 7f3bc60..b87a096 100644
--- a/storage/interface.go
+++ b/storage/interface.go
@@ -71,4 +71,10 @@ type Storage interface {
UpdateUser(user *happydns.User) error
DeleteUser(user *happydns.User) error
ClearUsers() error
+
+ GetZone(id int64) (*happydns.Zone, error)
+ CreateZone(zone *happydns.Zone) error
+ UpdateZone(zone *happydns.Zone) error
+ DeleteZone(zone *happydns.Zone) error
+ ClearZones() error
}
diff --git a/storage/leveldb/domain.go b/storage/leveldb/domain.go
index 35b67a9..632b615 100644
--- a/storage/leveldb/domain.go
+++ b/storage/leveldb/domain.go
@@ -132,6 +132,11 @@ func (s *LevelDBStorage) DeleteDomain(z *happydns.Domain) error {
}
func (s *LevelDBStorage) ClearDomains() error {
+ err := s.ClearZones()
+ if err != nil {
+ return err
+ }
+
tx, err := s.db.OpenTransaction()
if err != nil {
return err
diff --git a/storage/leveldb/zone.go b/storage/leveldb/zone.go
new file mode 100644
index 0000000..e54f5ca
--- /dev/null
+++ b/storage/leveldb/zone.go
@@ -0,0 +1,90 @@
+// Copyright or © or Copr. happyDNS (2020)
+//
+// contact@happydns.org
+//
+// This software is a computer program whose purpose is to provide a modern
+// interface to interact with DNS systems.
+//
+// This software is governed by the CeCILL license under French law and abiding
+// by the rules of distribution of free software. You can use, modify and/or
+// redistribute the software under the terms of the CeCILL license as
+// circulated by CEA, CNRS and INRIA at the following URL
+// "http://www.cecill.info".
+//
+// As a counterpart to the access to the source code and rights to copy, modify
+// and redistribute granted by the license, users are provided only with a
+// limited warranty and the software's author, the holder of the economic
+// rights, and the successive licensors have only limited liability.
+//
+// In this respect, the user's attention is drawn to the risks associated with
+// loading, using, modifying and/or developing or reproducing the software by
+// the user in light of its specific status of free software, that may mean
+// that it is complicated to manipulate, and that also therefore means that it
+// is reserved for developers and experienced professionals having in-depth
+// computer knowledge. Users are therefore encouraged to load and test the
+// software's suitability as regards their requirements in conditions enabling
+// the security of their systems and/or data to be ensured and, more generally,
+// to use and operate it in the same conditions as regards security.
+//
+// The fact that you are presently reading this means that you have had
+// knowledge of the CeCILL license and that you accept its terms.
+
+package database
+
+import (
+ "fmt"
+
+ "git.happydns.org/happydns/model"
+
+ "github.com/syndtr/goleveldb/leveldb/util"
+)
+
+func (s *LevelDBStorage) GetZone(id int64) (z *happydns.Zone, err error) {
+ z = &happydns.Zone{}
+ err = s.get(fmt.Sprintf("domain.zone-%d", id), &z)
+ return
+}
+
+func (s *LevelDBStorage) CreateZone(z *happydns.Zone) error {
+ key, id, err := s.findInt63Key("domain.zone-")
+ if err != nil {
+ return err
+ }
+
+ z.Id = id
+ return s.put(key, z)
+}
+
+func (s *LevelDBStorage) UpdateZone(z *happydns.Zone) error {
+ return s.put(fmt.Sprintf("domain.zone-%d", z.Id), z)
+}
+
+func (s *LevelDBStorage) DeleteZone(z *happydns.Zone) error {
+ return s.delete(fmt.Sprintf("domain.zone-%d", z.Id))
+}
+
+func (s *LevelDBStorage) ClearZones() error {
+ tx, err := s.db.OpenTransaction()
+ if err != nil {
+ return err
+ }
+
+ iter := tx.NewIterator(util.BytesPrefix([]byte("domain.zone-")), nil)
+ defer iter.Release()
+
+ for iter.Next() {
+ err = tx.Delete(iter.Key(), nil)
+ if err != nil {
+ tx.Discard()
+ return err
+ }
+ }
+
+ err = tx.Commit()
+ if err != nil {
+ tx.Discard()
+ return err
+ }
+
+ return nil
+}