diff --git a/api/domains.go b/api/domains.go new file mode 100644 index 0000000..288e03e --- /dev/null +++ b/api/domains.go @@ -0,0 +1,209 @@ +package api + +import ( + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + + "github.com/julienschmidt/httprouter" + "github.com/miekg/dns" + + "git.happydns.org/happydns/config" + "git.happydns.org/happydns/model" + "git.happydns.org/happydns/storage" +) + +func init() { + router.GET("/api/domains", apiAuthHandler(getDomains)) + router.POST("/api/domains", apiAuthHandler(addDomain)) + router.DELETE("/api/domains/:domain", apiAuthHandler(domainHandler(delDomain))) + router.GET("/api/domains/:domain", apiAuthHandler(domainHandler(getDomain))) + router.GET("/api/domains/:domain/rr", apiAuthHandler(domainHandler(axfrDomain))) + router.POST("/api/domains/:domain/rr", apiAuthHandler(domainHandler(addRR))) + router.DELETE("/api/domains/:domain/rr", apiAuthHandler(domainHandler(delRR))) +} + +func getDomains(_ *config.Options, u *happydns.User, p httprouter.Params, body io.Reader) Response { + if domains, err := storage.MainStore.GetDomains(u); err != nil { + return APIErrorResponse{ + err: err, + } + } else { + return APIResponse{ + response: domains, + } + } +} + +func addDomain(_ *config.Options, u *happydns.User, p httprouter.Params, body io.Reader) Response { + var uz happydns.Domain + err := json.NewDecoder(body).Decode(&uz) + if err != nil { + return APIErrorResponse{ + err: err, + } + } + + if len(uz.DomainName) <= 2 { + return APIErrorResponse{ + err: errors.New("The given domain is invalid."), + } + } + + if uz.DomainName[len(uz.DomainName)-1] != '.' { + uz.DomainName = uz.DomainName + "." + } + + if storage.MainStore.DomainExists(uz.DomainName) { + return APIErrorResponse{ + err: errors.New("This domain already exists."), + } + } else if err := storage.MainStore.CreateDomain(u, &uz); err != nil { + return APIErrorResponse{ + err: err, + } + } else { + return APIResponse{ + response: uz, + } + } +} + +func delDomain(_ *config.Options, domain *happydns.Domain, body io.Reader) Response { + if err := storage.MainStore.DeleteDomain(domain); err != nil { + return APIErrorResponse{ + err: err, + } + } else { + return APIResponse{ + response: true, + } + } +} + +func domainHandler(f func(*config.Options, *happydns.Domain, 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 { + if domain, err := storage.MainStore.GetDomainByDN(u, ps.ByName("domain")); err != nil { + return APIErrorResponse{ + status: http.StatusNotFound, + err: errors.New("Domain not found"), + } + } else { + return f(opts, domain, body) + } + } +} + +func getDomain(_ *config.Options, domain *happydns.Domain, body io.Reader) Response { + return APIResponse{ + response: domain, + } +} + +func axfrDomain(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, + } + } + + rrs, err := source.ImportZone(domain) + if err != nil { + return APIErrorResponse{ + err: err, + } + } + + var ret []map[string]interface{} + for _, rr := range rrs { + ret = append(ret, map[string]interface{}{ + "string": rr.String(), + "fields": rr, + }) + } + + return APIResponse{ + response: ret, + } +} + +type uploadedRR struct { + RR string `json:"string"` +} + +func addRR(opts *config.Options, domain *happydns.Domain, body io.Reader) Response { + var urr uploadedRR + err := json.NewDecoder(body).Decode(&urr) + if err != nil { + return APIErrorResponse{ + err: err, + } + } + + rr, err := dns.NewRR(fmt.Sprintf("$ORIGIN %s\n$TTL %d\n%s", domain.DomainName, 3600, urr.RR)) + if err != nil { + return APIErrorResponse{ + err: err, + } + } + + source, err := storage.MainStore.GetSource(&happydns.User{Id: domain.IdUser}, domain.IdSource) + if err != nil { + return APIErrorResponse{ + err: err, + } + } + + err = source.AddRR(domain, rr) + if err != nil { + return APIErrorResponse{ + status: http.StatusInternalServerError, + err: err, + } + } + + return APIResponse{ + response: map[string]interface{}{ + "string": rr.String(), + }, + } +} + +func delRR(opts *config.Options, domain *happydns.Domain, body io.Reader) Response { + var urr uploadedRR + err := json.NewDecoder(body).Decode(&urr) + if err != nil { + return APIErrorResponse{ + err: err, + } + } + + rr, err := dns.NewRR(urr.RR) + if err != nil { + return APIErrorResponse{ + err: err, + } + } + + source, err := storage.MainStore.GetSource(&happydns.User{Id: domain.IdUser}, domain.IdSource) + if err != nil { + return APIErrorResponse{ + err: err, + } + } + + err = source.DeleteRR(domain, rr) + if err != nil { + return APIErrorResponse{ + status: http.StatusInternalServerError, + err: err, + } + } + + return APIResponse{ + response: true, + } +} diff --git a/api/zones.go b/api/zones.go deleted file mode 100644 index 3e4a912..0000000 --- a/api/zones.go +++ /dev/null @@ -1,264 +0,0 @@ -package api - -import ( - "encoding/json" - "errors" - "fmt" - "io" - "net" - "net/http" - "strings" - "time" - - "github.com/julienschmidt/httprouter" - "github.com/miekg/dns" - - "git.happydns.org/happydns/config" - "git.happydns.org/happydns/model" - "git.happydns.org/happydns/storage" -) - -func init() { - router.GET("/api/zones", apiAuthHandler(getZones)) - router.POST("/api/zones", apiAuthHandler(addZone)) - router.DELETE("/api/zones/:zone", apiAuthHandler(zoneHandler(delZone))) - router.GET("/api/zones/:zone", apiAuthHandler(zoneHandler(getZone))) - router.GET("/api/zones/:zone/rr", apiAuthHandler(zoneHandler(axfrZone))) - router.POST("/api/zones/:zone/rr", apiAuthHandler(zoneHandler(addRR))) - router.DELETE("/api/zones/:zone/rr", apiAuthHandler(zoneHandler(delRR))) -} - -func getZones(_ *config.Options, u *happydns.User, p httprouter.Params, body io.Reader) Response { - if zones, err := storage.MainStore.GetZones(u); err != nil { - return APIErrorResponse{ - err: err, - } - } else { - return APIResponse{ - response: zones, - } - } -} - -func addZone(_ *config.Options, u *happydns.User, p httprouter.Params, body io.Reader) Response { - var uz happydns.Zone - err := json.NewDecoder(body).Decode(&uz) - if err != nil { - return APIErrorResponse{ - err: err, - } - } - - if len(uz.DomainName) <= 2 { - return APIErrorResponse{ - err: errors.New("The given zone is invalid."), - } - } - - if uz.DomainName[len(uz.DomainName)-1] != '.' { - uz.DomainName = uz.DomainName + "." - } - - if len(uz.KeyName) > 1 && uz.KeyName[len(uz.KeyName)-1] != '.' { - uz.KeyName = uz.KeyName + "." - } - - if storage.MainStore.ZoneExists(uz.DomainName) { - return APIErrorResponse{ - err: errors.New("This zone already exists."), - } - } else if err := storage.MainStore.CreateZone(u, &uz); err != nil { - return APIErrorResponse{ - err: err, - } - } else { - return APIResponse{ - response: uz, - } - } -} - -func delZone(_ *config.Options, zone *happydns.Zone, body io.Reader) Response { - if err := storage.MainStore.DeleteZone(zone); err != nil { - return APIErrorResponse{ - err: err, - } - } else { - return APIResponse{ - response: true, - } - } -} - -func zoneHandler(f func(*config.Options, *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 { - if zone, err := storage.MainStore.GetZoneByDN(u, ps.ByName("zone")); err != nil { - return APIErrorResponse{ - status: http.StatusNotFound, - err: errors.New("Domain not found"), - } - } else { - return f(opts, zone, body) - } - } -} - -func getZone(_ *config.Options, zone *happydns.Zone, body io.Reader) Response { - return APIResponse{ - response: zone, - } -} - -func normalizeNSServer(opts *config.Options, srv string) string { - if srv == "" { - return opts.DefaultNameServer - } else if strings.Index(srv, ":") > -1 { - return srv - } else { - return srv + ":53" - } -} - -func axfrZone(opts *config.Options, zone *happydns.Zone, body io.Reader) Response { - d := net.Dialer{} - con, err := d.Dial("tcp", normalizeNSServer(opts, zone.Server)) - if err != nil { - return APIErrorResponse{ - status: http.StatusInternalServerError, - err: err, - } - } - defer con.Close() - - m := new(dns.Msg) - m.SetEdns0(4096, true) - m.SetAxfr(zone.DomainName) - m.SetTsig(zone.KeyName, zone.KeyAlgo, 300, time.Now().Unix()) - - dnscon := &dns.Conn{Conn: con} - transfer := &dns.Transfer{Conn: dnscon, TsigSecret: map[string]string{zone.KeyName: zone.Base64KeyBlob()}} - c, err := transfer.In(m, normalizeNSServer(opts, zone.Server)) - - if err != nil { - return APIErrorResponse{ - status: http.StatusInternalServerError, - err: err, - } - } - - var rrs []map[string]interface{} - for { - response, ok := <-c - if !ok { - break - } - - for _, rr := range response.RR { - rrs = append(rrs, map[string]interface{}{ - "string": rr.String(), - "fields": rr, - }) - } - } - - if len(rrs) > 0 { - rrs = rrs[0 : len(rrs)-1] - } - - return APIResponse{ - response: rrs, - } -} - -type uploadedRR struct { - RR string `json:"string"` -} - -func addRR(opts *config.Options, zone *happydns.Zone, body io.Reader) Response { - var urr uploadedRR - err := json.NewDecoder(body).Decode(&urr) - if err != nil { - return APIErrorResponse{ - err: err, - } - } - - rr, err := dns.NewRR(fmt.Sprintf("$ORIGIN %s\n$TTL %d\n%s", zone.DomainName, 3600, urr.RR)) - if err != nil { - return APIErrorResponse{ - err: err, - } - } - - m := new(dns.Msg) - m.Id = dns.Id() - m.Opcode = dns.OpcodeUpdate - m.Question = make([]dns.Question, 1) - m.Question[0] = dns.Question{zone.DomainName, dns.TypeSOA, dns.ClassINET} - - m.Insert([]dns.RR{rr}) - - c := new(dns.Client) - c.TsigSecret = map[string]string{zone.KeyName: zone.Base64KeyBlob()} - m.SetTsig(zone.KeyName, zone.KeyAlgo, 300, time.Now().Unix()) - - in, rtt, err := c.Exchange(m, normalizeNSServer(opts, zone.Server)) - if err != nil { - return APIErrorResponse{ - status: http.StatusInternalServerError, - err: err, - } - } - - return APIResponse{ - response: map[string]interface{}{ - "in": *in, - "rtt": rtt, - "string": rr.String(), - }, - } -} - -func delRR(opts *config.Options, zone *happydns.Zone, body io.Reader) Response { - var urr uploadedRR - err := json.NewDecoder(body).Decode(&urr) - if err != nil { - return APIErrorResponse{ - err: err, - } - } - - rr, err := dns.NewRR(urr.RR) - if err != nil { - return APIErrorResponse{ - err: err, - } - } - - m := new(dns.Msg) - m.Id = dns.Id() - m.Opcode = dns.OpcodeUpdate - m.Question = make([]dns.Question, 1) - m.Question[0] = dns.Question{zone.DomainName, dns.TypeSOA, dns.ClassINET} - - m.Remove([]dns.RR{rr}) - - c := new(dns.Client) - c.TsigSecret = map[string]string{zone.KeyName: zone.Base64KeyBlob()} - m.SetTsig(zone.KeyName, zone.KeyAlgo, 300, time.Now().Unix()) - - in, rtt, err := c.Exchange(m, normalizeNSServer(opts, zone.Server)) - if err != nil { - return APIErrorResponse{ - status: http.StatusInternalServerError, - err: err, - } - } - - return APIResponse{ - response: map[string]interface{}{ - "in": *in, - "rtt": rtt, - }, - } -} diff --git a/htdocs/src/router/index.js b/htdocs/src/router/index.js index 427ec2a..5b4810e 100644 --- a/htdocs/src/router/index.js +++ b/htdocs/src/router/index.js @@ -26,39 +26,39 @@ const routes = [ } }, { - path: '/zones', - name: 'zones', + path: '/domains', + name: 'domains', component: function () { - return import(/* webpackChunkName: "zone-list" */ '../views/zone-list.vue') + return import(/* webpackChunkName: "domain-list" */ '../views/domain-list.vue') } }, { - path: '/zones/:zone', + path: '/domains/:zone', component: function () { - return import(/* webpackChunkName: "zone" */ '../views/zone.vue') + return import(/* webpackChunkName: "domain" */ '../views/domain.vue') }, children: [ { path: '', - name: 'zone', + name: 'domain-source', component: function () { - return import(/* webpackChunkName: "zone" */ '../views/zone-details.vue') + return import(/* webpackChunkName: "domain-source" */ '../views/domain-source.vue') } }, { path: 'services', name: 'zone-services', component: function () { - return import(/* webpackChunkName: "zone" */ '../views/zone-services.vue') + return import(/* webpackChunkName: "zone-services" */ '../views/zone-services.vue') } } ] }, { - path: '/zones/:zone/records', + path: '/zones/:domain/records', name: 'zone-records', component: function () { - return import(/* webpackChunkName: "zone" */ '../views/zone-records.vue') + return import(/* webpackChunkName: "zone-records" */ '../views/zone-records.vue') } } ] diff --git a/htdocs/src/views/Index.vue b/htdocs/src/views/Index.vue index 8910fdf..eb20012 100644 --- a/htdocs/src/views/Index.vue +++ b/htdocs/src/views/Index.vue @@ -4,7 +4,7 @@ diff --git a/htdocs/src/views/zone-records.vue b/htdocs/src/views/zone-records.vue index 1cc443e..0778bb8 100644 --- a/htdocs/src/views/zone-records.vue +++ b/htdocs/src/views/zone-records.vue @@ -5,8 +5,8 @@ DNSSEC - - {{ zone.domain }} + + {{ domain.domain }} Resource Records ({{ rrsFiltered.length }}/{{ rrs.length }}) Error: {{ error }} @@ -74,7 +74,7 @@ export default { expandrrs: {}, rrs: [], query: '', - zone: {} + domain: {} } }, @@ -94,13 +94,13 @@ export default { }, mounted () { - var myzone = this.$route.params.zone + var mydomain = this.$route.params.domain axios - .get('/api/zones/' + myzone) - .then(response => (this.zone = response.data)) + .get('/api/domains/' + mydomain) + .then(response => (this.domain = response.data)) axios - .get('/api/zones/' + myzone + '/rr') + .get('/api/domains/' + mydomain + '/rr') .then( (response) => (this.rrs = response.data), (error) => (this.error = error.response.data.errmsg) @@ -119,24 +119,24 @@ export default { newRR (idx) { axios - .post('/api/zones/' + this.$route.params.zone + '/rr', { + .post('/api/domains/' + this.$route.params.domain + '/rr', { string: this.rrsFiltered[idx].string }) .then( (response) => { axios - .get('/api/zones/' + this.$route.params.zone + '/rr') + .get('/api/domains/' + this.$route.params.domain + '/rr') .then(response => (this.rrs = response.data)) }, (error) => { - alert('An error occurs when trying to add RR to zone: ' + error.response.data.errmsg) + alert('An error occurs when trying to add RR to the zone: ' + error.response.data.errmsg) } ) }, deleteRR (idx) { axios - .delete('/api/zones/' + this.$route.params.zone + '/rr', { + .delete('/api/domains/' + this.$route.params.domain + '/rr', { data: { string: this.rrsFiltered[idx].string } @@ -144,11 +144,11 @@ export default { .then( (response) => { axios - .get('/api/zones/' + this.$route.params.zone + '/rr') + .get('/api/domains/' + this.$route.params.domain + '/rr') .then(response => (this.rrs = response.data)) }, (error) => { - alert('An error occurs when trying to delete RR in zone: ' + error.response.data.errmsg) + alert('An error occurs when trying to delete RR in the zone: ' + error.response.data.errmsg) } ) } diff --git a/htdocs/src/views/zone.vue b/htdocs/src/views/zone.vue deleted file mode 100644 index 0ca8522..0000000 --- a/htdocs/src/views/zone.vue +++ /dev/null @@ -1,74 +0,0 @@ - - - diff --git a/model/domain.go b/model/domain.go new file mode 100644 index 0000000..22c79f9 --- /dev/null +++ b/model/domain.go @@ -0,0 +1,34 @@ +package happydns + +import ( + "strings" +) + +type Domain struct { + Id int64 `json:"id"` + IdUser int64 + IdSource int64 + DomainName string `json:"domain"` +} + +type Domains []*Domain + +func (d *Domain) NormalizedNSServer() string { + if strings.Index(d.DomainName, ":") > -1 { + return d.DomainName + } else { + return d.DomainName + ":53" + } +} + +func NewDomain(u *User, s Source, dn string) (d *Domain) { + d = &Domain{ + IdUser: u.Id, + //IdSource: s.GetId(), + DomainName: dn, + } + + d.DomainName = d.NormalizedNSServer() + + return +} diff --git a/model/zone.go b/model/zone.go deleted file mode 100644 index 8bfff3a..0000000 --- a/model/zone.go +++ /dev/null @@ -1,34 +0,0 @@ -package happydns - -import ( - "encoding/base64" -) - -type Zone struct { - Id int64 `json:"id"` - IdUser int64 - DomainName string `json:"domain"` - Server string `json:"server,omitempty"` - KeyName string `json:"keyname,omitempty"` - KeyAlgo string `json:"algorithm,omitempty"` - KeyBlob []byte `json:"keyblob,omitempty"` - StorageFacility string `json:"storage_facility,omitempty"` -} - -type Zones []*Zone - -func NewZone(u User, dn, server, keyname, algo string, keyblob []byte, storage string) *Zone { - return &Zone{ - IdUser: u.Id, - DomainName: dn, - Server: server, - KeyName: keyname, - KeyAlgo: algo, - KeyBlob: keyblob, - StorageFacility: storage, - } -} - -func (z *Zone) Base64KeyBlob() string { - return base64.StdEncoding.EncodeToString(z.KeyBlob) -} diff --git a/static.go b/static.go index 0d70ce4..f696c19 100644 --- a/static.go +++ b/static.go @@ -33,6 +33,20 @@ func init() { fwd_request(w, r, opts.DevProxy) } }) + api.Router().GET("/domains/*_", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + opts := r.Context().Value("opts").(*config.Options) + + if opts.DevProxy == "" { + if data, err := Asset("htdocs/dist/index.html"); err != nil { + fmt.Fprintf(w, "{\"errmsg\":%q}", err) + } else { + w.Write(data) + } + } else { + r.URL.Path = "/" + fwd_request(w, r, opts.DevProxy) + } + }) api.Router().GET("/join", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { opts := r.Context().Value("opts").(*config.Options) diff --git a/storage/interface.go b/storage/interface.go index 374eb6c..70ddbe4 100644 --- a/storage/interface.go +++ b/storage/interface.go @@ -8,6 +8,16 @@ type Storage interface { DoMigration() error Close() error + GetDomains(u *happydns.User) (happydns.Domains, error) + GetDomain(u *happydns.User, id int) (*happydns.Domain, error) + GetDomainByDN(u *happydns.User, dn string) (*happydns.Domain, error) + DomainExists(dn string) bool + CreateDomain(u *happydns.User, z *happydns.Domain) error + UpdateDomain(z *happydns.Domain) error + UpdateDomainOwner(z *happydns.Domain, newOwner *happydns.User) error + DeleteDomain(z *happydns.Domain) error + ClearDomains() error + GetSession(id []byte) (*happydns.Session, error) CreateSession(session *happydns.Session) error UpdateSession(session *happydns.Session) error @@ -22,14 +32,4 @@ type Storage interface { UpdateUser(user *happydns.User) error DeleteUser(user *happydns.User) error ClearUsers() error - - GetZones(u *happydns.User) (happydns.Zones, error) - GetZone(u *happydns.User, id int) (*happydns.Zone, error) - GetZoneByDN(u *happydns.User, dn string) (*happydns.Zone, error) - ZoneExists(dn string) bool - CreateZone(u *happydns.User, z *happydns.Zone) error - UpdateZone(z *happydns.Zone) error - UpdateZoneOwner(z *happydns.Zone, newOwner *happydns.User) error - DeleteZone(z *happydns.Zone) error - ClearZones() error } diff --git a/storage/leveldb/domain.go b/storage/leveldb/domain.go new file mode 100644 index 0000000..1e653c6 --- /dev/null +++ b/storage/leveldb/domain.go @@ -0,0 +1,127 @@ +package database + +import ( + "fmt" + + "git.happydns.org/happydns/model" + + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/util" +) + +func (s *LevelDBStorage) GetDomains(u *happydns.User) (domains happydns.Domains, err error) { + iter := s.search("domain-") + defer iter.Release() + + for iter.Next() { + var z happydns.Domain + + err = decodeData(iter.Value(), &z) + if err != nil { + return + } + + if z.IdUser == u.Id { + domains = append(domains, &z) + } + } + + return +} + +func (s *LevelDBStorage) GetDomain(u *happydns.User, id int) (z *happydns.Domain, err error) { + z = &happydns.Domain{} + err = s.get(fmt.Sprintf("domain-%d", id), &z) + + if z.IdUser != u.Id { + z = nil + err = leveldb.ErrNotFound + } + + return +} + +func (s *LevelDBStorage) GetDomainByDN(u *happydns.User, dn string) (*happydns.Domain, error) { + domains, err := s.GetDomains(u) + if err != nil { + return nil, err + } + + for _, domain := range domains { + if domain.DomainName == dn { + return domain, nil + } + } + + return nil, leveldb.ErrNotFound +} + +func (s *LevelDBStorage) DomainExists(dn string) bool { + iter := s.search("domain-") + defer iter.Release() + + for iter.Next() { + var z happydns.Domain + + err := decodeData(iter.Value(), &z) + if err != nil { + continue + } + + if z.DomainName == dn { + return true + } + } + + return false +} + +func (s *LevelDBStorage) CreateDomain(u *happydns.User, z *happydns.Domain) error { + key, id, err := s.findInt63Key("domain-") + if err != nil { + return err + } + + z.Id = id + z.IdUser = u.Id + return s.put(key, z) +} + +func (s *LevelDBStorage) UpdateDomain(z *happydns.Domain) error { + return s.put(fmt.Sprintf("domain-%d", z.Id), z) +} + +func (s *LevelDBStorage) UpdateDomainOwner(z *happydns.Domain, newOwner *happydns.User) error { + z.IdUser = newOwner.Id + return s.put(fmt.Sprintf("domain-%d", z.Id), z) +} + +func (s *LevelDBStorage) DeleteDomain(z *happydns.Domain) error { + return s.delete(fmt.Sprintf("domain-%d", z.Id)) +} + +func (s *LevelDBStorage) ClearDomains() error { + tx, err := s.db.OpenTransaction() + if err != nil { + return err + } + + iter := tx.NewIterator(util.BytesPrefix([]byte("domain-")), 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 +} diff --git a/storage/leveldb/zone.go b/storage/leveldb/zone.go deleted file mode 100644 index 00413ab..0000000 --- a/storage/leveldb/zone.go +++ /dev/null @@ -1,127 +0,0 @@ -package database - -import ( - "fmt" - - "git.happydns.org/happydns/model" - - "github.com/syndtr/goleveldb/leveldb" - "github.com/syndtr/goleveldb/leveldb/util" -) - -func (s *LevelDBStorage) GetZones(u *happydns.User) (zones happydns.Zones, err error) { - iter := s.search("zone-") - defer iter.Release() - - for iter.Next() { - var z happydns.Zone - - err = decodeData(iter.Value(), &z) - if err != nil { - return - } - - if z.IdUser == u.Id { - zones = append(zones, &z) - } - } - - return -} - -func (s *LevelDBStorage) GetZone(u *happydns.User, id int) (z *happydns.Zone, err error) { - z = &happydns.Zone{} - err = s.get(fmt.Sprintf("zone-%d", id), &z) - - if z.IdUser != u.Id { - z = nil - err = leveldb.ErrNotFound - } - - return -} - -func (s *LevelDBStorage) GetZoneByDN(u *happydns.User, dn string) (*happydns.Zone, error) { - zones, err := s.GetZones(u) - if err != nil { - return nil, err - } - - for _, zone := range zones { - if zone.DomainName == dn { - return zone, nil - } - } - - return nil, leveldb.ErrNotFound -} - -func (s *LevelDBStorage) ZoneExists(dn string) bool { - iter := s.search("zone-") - defer iter.Release() - - for iter.Next() { - var z happydns.Zone - - err := decodeData(iter.Value(), &z) - if err != nil { - continue - } - - if z.DomainName == dn { - return true - } - } - - return false -} - -func (s *LevelDBStorage) CreateZone(u *happydns.User, z *happydns.Zone) error { - key, id, err := s.findInt63Key("zone-") - if err != nil { - return err - } - - z.Id = id - z.IdUser = u.Id - return s.put(key, z) -} - -func (s *LevelDBStorage) UpdateZone(z *happydns.Zone) error { - return s.put(fmt.Sprintf("zone-%d", z.Id), z) -} - -func (s *LevelDBStorage) UpdateZoneOwner(z *happydns.Zone, newOwner *happydns.User) error { - z.IdUser = newOwner.Id - return s.put(fmt.Sprintf("zone-%d", z.Id), z) -} - -func (s *LevelDBStorage) DeleteZone(z *happydns.Zone) error { - return s.delete(fmt.Sprintf("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("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 -} diff --git a/storage/mysql/domain.go b/storage/mysql/domain.go new file mode 100644 index 0000000..b23325b --- /dev/null +++ b/storage/mysql/domain.go @@ -0,0 +1,81 @@ +package database + +import ( + "git.happydns.org/happydns/model" +) + +func (s *MySQLStorage) GetDomains(u *happydns.User) (domains happydns.Domains, err error) { + if rows, errr := s.db.Query("SELECT id_domain, id_user, id_source, domain FROM domains WHERE id_user = ?", u.Id); errr != nil { + return nil, errr + } else { + defer rows.Close() + + for rows.Next() { + var z happydns.Domain + if err = rows.Scan(&z.Id, &z.IdUser, &z.IdSource, &z.DomainName); err != nil { + return + } + domains = append(domains, &z) + } + if err = rows.Err(); err != nil { + return + } + + return + } +} + +func (s *MySQLStorage) GetDomain(u *happydns.User, id int) (z *happydns.Domain, err error) { + z = &happydns.Domain{} + err = s.db.QueryRow("SELECT id_domain, id_user, id_source, domain FROM domains WHERE id_domain=? AND id_user=?", id, u.Id).Scan(&z.Id, &z.IdUser, &z.IdSource, &z.DomainName) + return +} + +func (s *MySQLStorage) GetDomainByDN(u *happydns.User, dn string) (z *happydns.Domain, err error) { + z = &happydns.Domain{} + err = s.db.QueryRow("SELECT id_domain, id_user, id_source, domain FROM domains WHERE domain=? AND id_user=?", dn, u.Id).Scan(&z.Id, &z.IdUser, &z.IdSource, &z.DomainName) + return +} + +func (s *MySQLStorage) DomainExists(dn string) bool { + var z int + err := s.db.QueryRow("SELECT 1 FROM domains WHERE domain=?", dn).Scan(&z) + return err == nil && z == 1 +} + +func (s *MySQLStorage) CreateDomain(u *happydns.User, src happydns.SourceType, z *happydns.Domain) error { + if res, err := s.db.Exec("INSERT INTO domains (id_user, id_source, domain) VALUES (?, ?, ?)", u.Id, src.Id, z.DomainName); err != nil { + return err + } else if z.Id, err = res.LastInsertId(); err != nil { + return err + } else { + return nil + } +} + +func (s *MySQLStorage) UpdateDomain(z *happydns.Domain) error { + if _, err := s.db.Exec("UPDATE domains SET id_source = ?, domain = ? WHERE id_domain = ?", z.IdSource, z.DomainName, z.Id); err != nil { + return err + } else { + return nil + } +} + +func (s *MySQLStorage) UpdateDomainOwner(z *happydns.Domain, newOwner *happydns.User) error { + if _, err := s.db.Exec("UPDATE domains SET id_user = ? WHERE id_domain = ?", newOwner.Id, z.Id); err != nil { + return err + } else { + z.IdUser = newOwner.Id + return nil + } +} + +func (s *MySQLStorage) DeleteDomain(z *happydns.Domain) error { + _, err := s.db.Exec("DELETE FROM domains WHERE id_domain = ?", z.Id) + return err +} + +func (s *MySQLStorage) ClearDomains() error { + _, err := s.db.Exec("DELETE FROM domains") + return err +} diff --git a/storage/mysql/schemas.go b/storage/mysql/schemas.go index 70c2fe4..724cc0b 100644 --- a/storage/mysql/schemas.go +++ b/storage/mysql/schemas.go @@ -2,7 +2,7 @@ package database // import "happydns.org/database" -const schemaVersion = 2 +const schemaVersion = 3 var schemaRevisions = map[uint16]string{ 1: `CREATE TABLE schema_version ( @@ -50,5 +50,18 @@ ALTER TABLE user_sessions ALTER TABLE zones CHANGE id_user id_user BIGINT NOT NULL; +`, + 3: `ALTER TABLE zones + DROP COLUMN server; +ALTER TABLE zones + DROP COLUMN key_name; +ALTER TABLE zones + DROP COLUMN key_algo; +ALTER TABLE zones + DROP COLUMN key_blob; +ALTER TABLE zones + DROP COLUMN storage_facility; + +RENAME TABLE zones TO domains; `, } diff --git a/storage/mysql/schemas/003.sql b/storage/mysql/schemas/003.sql new file mode 100644 index 0000000..70f7195 --- /dev/null +++ b/storage/mysql/schemas/003.sql @@ -0,0 +1,12 @@ +ALTER TABLE zones + DROP COLUMN server; +ALTER TABLE zones + DROP COLUMN key_name; +ALTER TABLE zones + DROP COLUMN key_algo; +ALTER TABLE zones + DROP COLUMN key_blob; +ALTER TABLE zones + DROP COLUMN storage_facility; + +RENAME TABLE zones TO domains; diff --git a/storage/mysql/zone.go b/storage/mysql/zone.go deleted file mode 100644 index c6ddff7..0000000 --- a/storage/mysql/zone.go +++ /dev/null @@ -1,81 +0,0 @@ -package database - -import ( - "git.happydns.org/happydns/model" -) - -func (s *MySQLStorage) GetZones(u *happydns.User) (zones happydns.Zones, err error) { - if rows, errr := s.db.Query("SELECT id_zone, id_user, domain, server, key_name, key_algo, key_blob, storage_facility FROM zones WHERE id_user = ?", u.Id); errr != nil { - return nil, errr - } else { - defer rows.Close() - - for rows.Next() { - var z happydns.Zone - if err = rows.Scan(&z.Id, &z.IdUser, &z.DomainName, &z.Server, &z.KeyName, &z.KeyAlgo, &z.KeyBlob, &z.StorageFacility); err != nil { - return - } - zones = append(zones, &z) - } - if err = rows.Err(); err != nil { - return - } - - return - } -} - -func (s *MySQLStorage) GetZone(u *happydns.User, id int) (z *happydns.Zone, err error) { - z = &happydns.Zone{} - err = s.db.QueryRow("SELECT id_zone, id_user, domain, server, key_name, key_algo, key_blob, storage_facility FROM zones WHERE id_zone=? AND id_user=?", id, u.Id).Scan(&z.Id, &z.IdUser, &z.DomainName, &z.Server, &z.KeyName, &z.KeyAlgo, &z.KeyBlob, &z.StorageFacility) - return -} - -func (s *MySQLStorage) GetZoneByDN(u *happydns.User, dn string) (z *happydns.Zone, err error) { - z = &happydns.Zone{} - err = s.db.QueryRow("SELECT id_zone, id_user, domain, server, key_name, key_algo, key_blob, storage_facility FROM zones WHERE domain=? AND id_user=?", dn, u.Id).Scan(&z.Id, &z.IdUser, &z.DomainName, &z.Server, &z.KeyName, &z.KeyAlgo, &z.KeyBlob, &z.StorageFacility) - return -} - -func (s *MySQLStorage) ZoneExists(dn string) bool { - var z int - err := s.db.QueryRow("SELECT 1 FROM zones WHERE domain=?", dn).Scan(&z) - return err == nil && z == 1 -} - -func (s *MySQLStorage) CreateZone(u *happydns.User, z *happydns.Zone) error { - if res, err := s.db.Exec("INSERT INTO zones (id_user, domain, server, key_name, key_blob, storage_facility) VALUES (?, ?, ?, ?, ?, ?)", u.Id, z.DomainName, z.Server, z.KeyName, z.KeyBlob, z.StorageFacility); err != nil { - return err - } else if z.Id, err = res.LastInsertId(); err != nil { - return err - } else { - return nil - } -} - -func (s *MySQLStorage) UpdateZone(z *happydns.Zone) error { - if _, err := s.db.Exec("UPDATE zones SET domain = ?, key_name = ?, key_algo = ?, key_blob = ?, storage_facility = ? WHERE id_zone = ?", z.DomainName, z.KeyName, z.KeyAlgo, z.KeyBlob, z.StorageFacility, z.Id); err != nil { - return err - } else { - return nil - } -} - -func (s *MySQLStorage) UpdateZoneOwner(z *happydns.Zone, newOwner *happydns.User) error { - if _, err := s.db.Exec("UPDATE zones SET id_user = ? WHERE id_zone = ?", newOwner.Id, z.Id); err != nil { - return err - } else { - z.IdUser = newOwner.Id - return nil - } -} - -func (s *MySQLStorage) DeleteZone(z *happydns.Zone) error { - _, err := s.db.Exec("DELETE FROM zones WHERE id_zone = ?", z.Id) - return err -} - -func (s *MySQLStorage) ClearZones() error { - _, err := s.db.Exec("DELETE FROM zones") - return err -}