mirror of
https://github.com/TeaOSLab/EdgeAPI.git
synced 2025-11-03 23:20:26 +08:00
域名操作错误时显示具体的域名、记录信息等
This commit is contained in:
@@ -150,13 +150,13 @@ func (this *AliDNSProvider) AddRecord(domain string, newRecord *dnstypes.Record)
|
|||||||
resp := alidns.CreateAddDomainRecordResponse()
|
resp := alidns.CreateAddDomainRecordResponse()
|
||||||
err := this.doAPI(req, resp)
|
err := this.doAPI(req, resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return this.WrapError(err, domain, newRecord)
|
||||||
}
|
}
|
||||||
if resp.IsSuccess() {
|
if resp.IsSuccess() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.New(resp.GetHttpContentString())
|
return this.WrapError(errors.New(resp.GetHttpContentString()), domain, newRecord)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateRecord 修改记录
|
// UpdateRecord 修改记录
|
||||||
@@ -174,7 +174,7 @@ func (this *AliDNSProvider) UpdateRecord(domain string, record *dnstypes.Record,
|
|||||||
|
|
||||||
resp := alidns.CreateUpdateDomainRecordResponse()
|
resp := alidns.CreateUpdateDomainRecordResponse()
|
||||||
err := this.doAPI(req, resp)
|
err := this.doAPI(req, resp)
|
||||||
return err
|
return this.WrapError(err, domain, newRecord)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteRecord 删除记录
|
// DeleteRecord 删除记录
|
||||||
@@ -184,7 +184,7 @@ func (this *AliDNSProvider) DeleteRecord(domain string, record *dnstypes.Record)
|
|||||||
|
|
||||||
resp := alidns.CreateDeleteDomainRecordResponse()
|
resp := alidns.CreateDeleteDomainRecordResponse()
|
||||||
err := this.doAPI(req, resp)
|
err := this.doAPI(req, resp)
|
||||||
return err
|
return this.WrapError(err, domain, record)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultRoute 默认线路
|
// DefaultRoute 默认线路
|
||||||
|
|||||||
@@ -1,4 +1,29 @@
|
|||||||
package dnsclients
|
package dnsclients
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients/dnstypes"
|
||||||
|
"github.com/iwind/TeaGo/types"
|
||||||
|
)
|
||||||
|
|
||||||
type BaseProvider struct {
|
type BaseProvider struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WrapError 封装解析相关错误
|
||||||
|
func (this *BaseProvider) WrapError(err error, domain string, record *dnstypes.Record) error {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if record == nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var fullname = ""
|
||||||
|
if len(record.Name) == 0 {
|
||||||
|
fullname = domain
|
||||||
|
} else {
|
||||||
|
fullname = record.Name + "." + domain
|
||||||
|
}
|
||||||
|
return errors.New("record operation failed: '" + fullname + " " + record.Type + " " + record.Value + " " + types.String(record.TTL) + "': " + err.Error())
|
||||||
|
}
|
||||||
|
|||||||
38
internal/dnsclients/provider_base_test.go
Normal file
38
internal/dnsclients/provider_base_test.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright 2022 Liuxiangchao iwind.liu@gmail.com. All rights reserved.
|
||||||
|
|
||||||
|
package dnsclients_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients"
|
||||||
|
"github.com/TeaOSLab/EdgeAPI/internal/dnsclients/dnstypes"
|
||||||
|
"github.com/TeaOSLab/EdgeAPI/internal/errors"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBaseProvider_WrapError(t *testing.T) {
|
||||||
|
var provider = &dnsclients.BaseProvider{}
|
||||||
|
t.Log(provider.WrapError(nil, "example.com", &dnstypes.Record{
|
||||||
|
Id: "",
|
||||||
|
Name: "a",
|
||||||
|
Type: "A",
|
||||||
|
Value: "192.168.1.100",
|
||||||
|
Route: "",
|
||||||
|
TTL: 3600,
|
||||||
|
}))
|
||||||
|
t.Log(provider.WrapError(errors.New("fake error"), "example.com", &dnstypes.Record{
|
||||||
|
Id: "",
|
||||||
|
Name: "a",
|
||||||
|
Type: "A",
|
||||||
|
Value: "192.168.1.100",
|
||||||
|
Route: "",
|
||||||
|
TTL: 3600,
|
||||||
|
}))
|
||||||
|
t.Log(provider.WrapError(errors.New("fake error"), "example.com", &dnstypes.Record{
|
||||||
|
Id: "",
|
||||||
|
Name: "",
|
||||||
|
Type: "A",
|
||||||
|
Value: "192.168.1.100",
|
||||||
|
Route: "",
|
||||||
|
TTL: 3600,
|
||||||
|
}))
|
||||||
|
}
|
||||||
@@ -169,7 +169,7 @@ func (this *CloudFlareProvider) QueryRecord(domain string, name string, recordTy
|
|||||||
func (this *CloudFlareProvider) AddRecord(domain string, newRecord *dnstypes.Record) error {
|
func (this *CloudFlareProvider) AddRecord(domain string, newRecord *dnstypes.Record) error {
|
||||||
zoneId, err := this.findZoneIdWithDomain(domain)
|
zoneId, err := this.findZoneIdWithDomain(domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return this.WrapError(err, domain, newRecord)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := new(cloudflare.CreateDNSRecordResponse)
|
resp := new(cloudflare.CreateDNSRecordResponse)
|
||||||
@@ -186,7 +186,7 @@ func (this *CloudFlareProvider) AddRecord(domain string, newRecord *dnstypes.Rec
|
|||||||
"ttl": ttl,
|
"ttl": ttl,
|
||||||
}, resp)
|
}, resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return this.WrapError(err, domain, newRecord)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -195,7 +195,7 @@ func (this *CloudFlareProvider) AddRecord(domain string, newRecord *dnstypes.Rec
|
|||||||
func (this *CloudFlareProvider) UpdateRecord(domain string, record *dnstypes.Record, newRecord *dnstypes.Record) error {
|
func (this *CloudFlareProvider) UpdateRecord(domain string, record *dnstypes.Record, newRecord *dnstypes.Record) error {
|
||||||
zoneId, err := this.findZoneIdWithDomain(domain)
|
zoneId, err := this.findZoneIdWithDomain(domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return this.WrapError(err, domain, newRecord)
|
||||||
}
|
}
|
||||||
|
|
||||||
var ttl = newRecord.TTL
|
var ttl = newRecord.TTL
|
||||||
@@ -216,13 +216,13 @@ func (this *CloudFlareProvider) UpdateRecord(domain string, record *dnstypes.Rec
|
|||||||
func (this *CloudFlareProvider) DeleteRecord(domain string, record *dnstypes.Record) error {
|
func (this *CloudFlareProvider) DeleteRecord(domain string, record *dnstypes.Record) error {
|
||||||
zoneId, err := this.findZoneIdWithDomain(domain)
|
zoneId, err := this.findZoneIdWithDomain(domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return this.WrapError(err, domain, record)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := new(cloudflare.DeleteDNSRecordResponse)
|
resp := new(cloudflare.DeleteDNSRecordResponse)
|
||||||
err = this.doAPI(http.MethodDelete, "zones/"+zoneId+"/dns_records/"+record.Id, map[string]string{}, nil, resp)
|
err = this.doAPI(http.MethodDelete, "zones/"+zoneId+"/dns_records/"+record.Id, map[string]string{}, nil, resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return this.WrapError(err, domain, record)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ var customHTTPClient = &http.Client{
|
|||||||
type CustomHTTPProvider struct {
|
type CustomHTTPProvider struct {
|
||||||
url string
|
url string
|
||||||
secret string
|
secret string
|
||||||
|
|
||||||
|
BaseProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auth 认证
|
// Auth 认证
|
||||||
@@ -117,7 +119,7 @@ func (this *CustomHTTPProvider) AddRecord(domain string, newRecord *dnstypes.Rec
|
|||||||
"domain": domain,
|
"domain": domain,
|
||||||
"newRecord": newRecord,
|
"newRecord": newRecord,
|
||||||
})
|
})
|
||||||
return err
|
return this.WrapError(err, domain, newRecord)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateRecord 修改记录
|
// UpdateRecord 修改记录
|
||||||
@@ -128,7 +130,7 @@ func (this *CustomHTTPProvider) UpdateRecord(domain string, record *dnstypes.Rec
|
|||||||
"record": record,
|
"record": record,
|
||||||
"newRecord": newRecord,
|
"newRecord": newRecord,
|
||||||
})
|
})
|
||||||
return err
|
return this.WrapError(err, domain, newRecord)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteRecord 删除记录
|
// DeleteRecord 删除记录
|
||||||
@@ -138,7 +140,7 @@ func (this *CustomHTTPProvider) DeleteRecord(domain string, record *dnstypes.Rec
|
|||||||
"domain": domain,
|
"domain": domain,
|
||||||
"record": record,
|
"record": record,
|
||||||
})
|
})
|
||||||
return err
|
return this.WrapError(err, domain, record)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultRoute 默认线路
|
// DefaultRoute 默认线路
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ func (this *DNSPodProvider) AddRecord(domain string, newRecord *dnstypes.Record)
|
|||||||
args["ttl"] = types.String(newRecord.TTL)
|
args["ttl"] = types.String(newRecord.TTL)
|
||||||
}
|
}
|
||||||
_, err := this.post("/Record.Create", args)
|
_, err := this.post("/Record.Create", args)
|
||||||
return err
|
return this.WrapError(err, domain, newRecord)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateRecord 修改记录
|
// UpdateRecord 修改记录
|
||||||
@@ -211,7 +211,7 @@ func (this *DNSPodProvider) UpdateRecord(domain string, record *dnstypes.Record,
|
|||||||
args["ttl"] = types.String(newRecord.TTL)
|
args["ttl"] = types.String(newRecord.TTL)
|
||||||
}
|
}
|
||||||
_, err := this.post("/Record.Modify", args)
|
_, err := this.post("/Record.Modify", args)
|
||||||
return err
|
return this.WrapError(err, domain, newRecord)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteRecord 删除记录
|
// DeleteRecord 删除记录
|
||||||
@@ -225,7 +225,7 @@ func (this *DNSPodProvider) DeleteRecord(domain string, record *dnstypes.Record)
|
|||||||
"record_id": record.Id,
|
"record_id": record.Id,
|
||||||
})
|
})
|
||||||
|
|
||||||
return err
|
return this.WrapError(err, domain, record)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送请求
|
// 发送请求
|
||||||
|
|||||||
@@ -1331,7 +1331,7 @@ func (this *HuaweiDNSProvider) QueryRecord(domain string, name string, recordTyp
|
|||||||
func (this *HuaweiDNSProvider) AddRecord(domain string, newRecord *dnstypes.Record) error {
|
func (this *HuaweiDNSProvider) AddRecord(domain string, newRecord *dnstypes.Record) error {
|
||||||
zoneId, err := this.findZoneIdWithDomain(domain)
|
zoneId, err := this.findZoneIdWithDomain(domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return this.WrapError(err, domain, newRecord)
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp = new(huaweidns.ZonesCreateRecordSetResponse)
|
var resp = new(huaweidns.ZonesCreateRecordSetResponse)
|
||||||
@@ -1354,7 +1354,7 @@ func (this *HuaweiDNSProvider) AddRecord(domain string, newRecord *dnstypes.Reco
|
|||||||
"ttl": ttl,
|
"ttl": ttl,
|
||||||
}, resp)
|
}, resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return this.WrapError(err, domain, newRecord)
|
||||||
}
|
}
|
||||||
|
|
||||||
newRecord.Id = resp.Id + "@" + newRecord.Value
|
newRecord.Id = resp.Id + "@" + newRecord.Value
|
||||||
@@ -1366,7 +1366,7 @@ func (this *HuaweiDNSProvider) AddRecord(domain string, newRecord *dnstypes.Reco
|
|||||||
func (this *HuaweiDNSProvider) UpdateRecord(domain string, record *dnstypes.Record, newRecord *dnstypes.Record) error {
|
func (this *HuaweiDNSProvider) UpdateRecord(domain string, record *dnstypes.Record, newRecord *dnstypes.Record) error {
|
||||||
zoneId, err := this.findZoneIdWithDomain(domain)
|
zoneId, err := this.findZoneIdWithDomain(domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return this.WrapError(err, domain, newRecord)
|
||||||
}
|
}
|
||||||
|
|
||||||
var recordId string
|
var recordId string
|
||||||
@@ -1397,7 +1397,7 @@ func (this *HuaweiDNSProvider) UpdateRecord(domain string, record *dnstypes.Reco
|
|||||||
"ttl": ttl,
|
"ttl": ttl,
|
||||||
}, resp)
|
}, resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return this.WrapError(err, domain, newRecord)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -1407,7 +1407,7 @@ func (this *HuaweiDNSProvider) UpdateRecord(domain string, record *dnstypes.Reco
|
|||||||
func (this *HuaweiDNSProvider) DeleteRecord(domain string, record *dnstypes.Record) error {
|
func (this *HuaweiDNSProvider) DeleteRecord(domain string, record *dnstypes.Record) error {
|
||||||
zoneId, err := this.findZoneIdWithDomain(domain)
|
zoneId, err := this.findZoneIdWithDomain(domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return this.WrapError(err, domain, record)
|
||||||
}
|
}
|
||||||
|
|
||||||
var recordId string
|
var recordId string
|
||||||
@@ -1421,7 +1421,7 @@ func (this *HuaweiDNSProvider) DeleteRecord(domain string, record *dnstypes.Reco
|
|||||||
var resp = new(huaweidns.ZonesDeleteRecordSetResponse)
|
var resp = new(huaweidns.ZonesDeleteRecordSetResponse)
|
||||||
err = this.doAPI(http.MethodDelete, "/v2.1/zones/"+zoneId+"/recordsets/"+recordId, map[string]string{}, maps.Map{}, resp)
|
err = this.doAPI(http.MethodDelete, "/v2.1/zones/"+zoneId+"/recordsets/"+recordId, map[string]string{}, maps.Map{}, resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return this.WrapError(err, domain, record)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import (
|
|||||||
type LocalEdgeDNSProvider struct {
|
type LocalEdgeDNSProvider struct {
|
||||||
clusterId int64 // 集群ID
|
clusterId int64 // 集群ID
|
||||||
ttl int32 // TTL
|
ttl int32 // TTL
|
||||||
|
|
||||||
|
BaseProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auth 认证
|
// Auth 认证
|
||||||
@@ -193,10 +195,10 @@ func (this *LocalEdgeDNSProvider) AddRecord(domain string, newRecord *dnstypes.R
|
|||||||
var tx *dbs.Tx
|
var tx *dbs.Tx
|
||||||
domainId, err := nameservers.SharedNSDomainDAO.FindDomainIdWithName(tx, this.clusterId, domain)
|
domainId, err := nameservers.SharedNSDomainDAO.FindDomainIdWithName(tx, this.clusterId, domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return this.WrapError(err, domain, newRecord)
|
||||||
}
|
}
|
||||||
if domainId == 0 {
|
if domainId == 0 {
|
||||||
return errors.New("can not find domain '" + domain + "'")
|
return this.WrapError(errors.New("can not find domain '"+domain+"'"), domain, newRecord)
|
||||||
}
|
}
|
||||||
|
|
||||||
var routeIds = []string{}
|
var routeIds = []string{}
|
||||||
@@ -209,7 +211,7 @@ func (this *LocalEdgeDNSProvider) AddRecord(domain string, newRecord *dnstypes.R
|
|||||||
}
|
}
|
||||||
_, err = nameservers.SharedNSRecordDAO.CreateRecord(tx, domainId, "", newRecord.Name, newRecord.Type, newRecord.Value, newRecord.TTL, routeIds)
|
_, err = nameservers.SharedNSRecordDAO.CreateRecord(tx, domainId, "", newRecord.Name, newRecord.Type, newRecord.Value, newRecord.TTL, routeIds)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return this.WrapError(err, domain, newRecord)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@@ -220,7 +222,7 @@ func (this *LocalEdgeDNSProvider) UpdateRecord(domain string, record *dnstypes.R
|
|||||||
var tx *dbs.Tx
|
var tx *dbs.Tx
|
||||||
domainId, err := nameservers.SharedNSDomainDAO.FindDomainIdWithName(tx, this.clusterId, domain)
|
domainId, err := nameservers.SharedNSDomainDAO.FindDomainIdWithName(tx, this.clusterId, domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return this.WrapError(err, domain, newRecord)
|
||||||
}
|
}
|
||||||
if domainId == 0 {
|
if domainId == 0 {
|
||||||
return errors.New("can not find domain '" + domain + "'")
|
return errors.New("can not find domain '" + domain + "'")
|
||||||
@@ -238,17 +240,17 @@ func (this *LocalEdgeDNSProvider) UpdateRecord(domain string, record *dnstypes.R
|
|||||||
if len(record.Id) > 0 {
|
if len(record.Id) > 0 {
|
||||||
err = nameservers.SharedNSRecordDAO.UpdateRecord(tx, types.Int64(record.Id), "", newRecord.Name, newRecord.Type, newRecord.Value, newRecord.TTL, routeIds, true)
|
err = nameservers.SharedNSRecordDAO.UpdateRecord(tx, types.Int64(record.Id), "", newRecord.Name, newRecord.Type, newRecord.Value, newRecord.TTL, routeIds, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return this.WrapError(err, domain, newRecord)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
realRecord, err := nameservers.SharedNSRecordDAO.FindEnabledRecordWithName(tx, domainId, record.Name, record.Type)
|
realRecord, err := nameservers.SharedNSRecordDAO.FindEnabledRecordWithName(tx, domainId, record.Name, record.Type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return this.WrapError(err, domain, newRecord)
|
||||||
}
|
}
|
||||||
if realRecord != nil {
|
if realRecord != nil {
|
||||||
err = nameservers.SharedNSRecordDAO.UpdateRecord(tx, types.Int64(realRecord.Id), "", newRecord.Name, newRecord.Type, newRecord.Value, newRecord.TTL, routeIds, true)
|
err = nameservers.SharedNSRecordDAO.UpdateRecord(tx, types.Int64(realRecord.Id), "", newRecord.Name, newRecord.Type, newRecord.Value, newRecord.TTL, routeIds, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return this.WrapError(err, domain, newRecord)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -261,7 +263,7 @@ func (this *LocalEdgeDNSProvider) DeleteRecord(domain string, record *dnstypes.R
|
|||||||
var tx *dbs.Tx
|
var tx *dbs.Tx
|
||||||
domainId, err := nameservers.SharedNSDomainDAO.FindDomainIdWithName(tx, this.clusterId, domain)
|
domainId, err := nameservers.SharedNSDomainDAO.FindDomainIdWithName(tx, this.clusterId, domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return this.WrapError(err, domain, record)
|
||||||
}
|
}
|
||||||
if domainId == 0 {
|
if domainId == 0 {
|
||||||
return errors.New("can not find domain '" + domain + "'")
|
return errors.New("can not find domain '" + domain + "'")
|
||||||
@@ -270,17 +272,17 @@ func (this *LocalEdgeDNSProvider) DeleteRecord(domain string, record *dnstypes.R
|
|||||||
if len(record.Id) > 0 {
|
if len(record.Id) > 0 {
|
||||||
err = nameservers.SharedNSRecordDAO.DisableNSRecord(tx, types.Int64(record.Id))
|
err = nameservers.SharedNSRecordDAO.DisableNSRecord(tx, types.Int64(record.Id))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return this.WrapError(err, domain, record)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
realRecord, err := nameservers.SharedNSRecordDAO.FindEnabledRecordWithName(tx, domainId, record.Name, record.Type)
|
realRecord, err := nameservers.SharedNSRecordDAO.FindEnabledRecordWithName(tx, domainId, record.Name, record.Type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return this.WrapError(err, domain, record)
|
||||||
}
|
}
|
||||||
if realRecord != nil {
|
if realRecord != nil {
|
||||||
err = nameservers.SharedNSRecordDAO.DisableNSRecord(tx, types.Int64(realRecord.Id))
|
err = nameservers.SharedNSRecordDAO.DisableNSRecord(tx, types.Int64(realRecord.Id))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return this.WrapError(err, domain, record)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user