增加自定义绑定域名

This commit is contained in:
2026-09-02 15:11:32 +08:00
parent db519d9af2
commit f644c278c9
23 changed files with 2966 additions and 212 deletions
+141 -31
View File
@@ -323,20 +323,31 @@ func (c *BackendTenantDomainController) Index() {
tid, _ := c.GetUint64("tid")
statusStr := strings.TrimSpace(c.GetString("status"))
subDomain := strings.TrimSpace(c.GetString("sub_domain"))
keyword := strings.TrimSpace(c.GetString("sub_domain"))
domainTypeStr := strings.TrimSpace(c.GetString("domain_type"))
qs := models.Orm.QueryTable(new(models.SystemTenantDomain)).Filter("delete_time__isnull", true)
// 条件一次性拼好:SetCond 会覆盖 Filter 累积的条件,不能混用
cond := orm.NewCondition().And("delete_time__isnull", true)
if tid > 0 {
qs = qs.Filter("tid", tid)
cond = cond.And("tid", tid)
}
if statusStr != "" {
if st, err := strconv.Atoi(statusStr); err == nil {
qs = qs.Filter("status", st)
cond = cond.And("status", st)
}
}
if subDomain != "" {
qs = qs.Filter("sub_domain__icontains", subDomain)
if domainTypeStr != "" {
if dt, err := strconv.Atoi(domainTypeStr); err == nil {
cond = cond.And("domain_type", dt)
}
}
// 关键字同时匹配二级前缀与完整域名:自有域名没有 sub_domain,只能靠 full_domain 命中
if keyword != "" {
cond = cond.AndCond(orm.NewCondition().
Or("sub_domain__icontains", keyword).
Or("full_domain__icontains", keyword))
}
qs := models.Orm.QueryTable(new(models.SystemTenantDomain)).SetCond(cond)
total, err := qs.Count()
if err != nil {
@@ -349,8 +360,7 @@ func (c *BackendTenantDomainController) Index() {
jsonErr(&c.Controller, 500, 500, "获取租户域名失败: "+err.Error())
return
}
list := make([]models.SystemTenantDomain, 0, len(rows))
list = append(list, rows...)
list := tenantDomainList(rows)
c.Data["json"] = map[string]interface{}{
"code": 200,
"msg": "success",
@@ -359,20 +369,21 @@ func (c *BackendTenantDomainController) Index() {
_ = c.ServeJSON()
}
// MyDomains GET /backend/domain/tenant/myDomains?tid=1
// MyDomains GET /backend/domain/tenant/myDomains
// 租户 ID 取登录态,忽略请求参数里的 tid,避免越权查看其他租户域名。
func (c *BackendTenantDomainController) MyDomains() {
if _, err := requireBackend(&c.Controller); err != nil {
claims, err := requireBackend(&c.Controller)
if err != nil {
jsonErr(&c.Controller, 401, 401, err.Error())
return
}
tid, _ := c.GetUint64("tid")
if tid == 0 {
jsonErr(&c.Controller, 400, 400, "租户ID不能为空")
if claims.TenantId <= 0 {
jsonErr(&c.Controller, 400, 400, "当前登录账号未归属租户")
return
}
var rows []models.SystemTenantDomain
_, err := models.Orm.QueryTable(new(models.SystemTenantDomain)).
Filter("tid", tid).
_, err = models.Orm.QueryTable(new(models.SystemTenantDomain)).
Filter("tid", claims.TenantId).
Filter("delete_time__isnull", true).
OrderBy("-id").
All(&rows)
@@ -380,23 +391,101 @@ func (c *BackendTenantDomainController) MyDomains() {
jsonErr(&c.Controller, 500, 500, "获取失败: "+err.Error())
return
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": rows}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": tenantDomainList(rows)}
_ = c.ServeJSON()
}
// Apply POST /backend/domain/tenant/apply body:{tid,sub_domain,main_domain}
func (c *BackendTenantDomainController) Apply() {
// BindCustom POST /backend/domain/tenant/bindCustom body:{domain}
// 绑定租户自有域名。租户 ID 取登录态。
func (c *BackendTenantDomainController) BindCustom() {
claims, err := requireBackend(&c.Controller)
if err != nil {
jsonErr(&c.Controller, 401, 401, err.Error())
return
}
if claims.TenantId <= 0 {
jsonErr(&c.Controller, 400, 400, "当前登录账号未归属租户")
return
}
raw, err := io.ReadAll(c.Ctx.Request.Body)
if err != nil {
jsonErr(&c.Controller, 400, 400, "参数错误")
return
}
var p customDomainPayload
if err := json.Unmarshal(raw, &p); err != nil {
jsonErr(&c.Controller, 400, 400, "参数错误")
return
}
id, status, errMsg := bindCustomDomain(uint64(claims.TenantId), p.Domain)
if errMsg != "" {
jsonErr(&c.Controller, status, status, errMsg)
return
}
c.Data["json"] = map[string]interface{}{
"code": 200,
"msg": "绑定成功,请按提示配置解析后点击「检测解析」",
"data": map[string]interface{}{"id": id},
}
_ = c.ServeJSON()
}
// CheckCustomDns POST /backend/domain/tenant/checkCustomDns body:{id}
// 检测自有域名解析,通过后异步申请 HTTPS 证书。
func (c *BackendTenantDomainController) CheckCustomDns() {
claims, err := requireBackend(&c.Controller)
if err != nil {
jsonErr(&c.Controller, 401, 401, err.Error())
return
}
if claims.TenantId <= 0 {
jsonErr(&c.Controller, 400, 400, "当前登录账号未归属租户")
return
}
raw, err := io.ReadAll(c.Ctx.Request.Body)
if err != nil {
jsonErr(&c.Controller, 400, 400, "参数错误")
return
}
var p domainIDPayload
if err := json.Unmarshal(raw, &p); err != nil {
jsonErr(&c.Controller, 400, 400, "参数错误")
return
}
data, status, errMsg := checkCustomDomainDNS(p.ID, uint64(claims.TenantId))
if errMsg != "" {
jsonErr(&c.Controller, status, status, errMsg)
return
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": data}
_ = c.ServeJSON()
}
// CustomDomainGuide GET /backend/domain/tenant/customDomainGuide
// 返回租户该怎么配解析(CNAME 目标 / A 记录 IP),前端不硬编码。
func (c *BackendTenantDomainController) CustomDomainGuide() {
if _, err := requireBackend(&c.Controller); err != nil {
jsonErr(&c.Controller, 401, 401, err.Error())
return
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": customDomainGuideData()}
_ = c.ServeJSON()
}
// Apply POST /backend/domain/tenant/apply body:{sub_domain,main_domain}
// 租户 ID 取登录态,不信请求体,避免替其他租户申请。
func (c *BackendTenantDomainController) Apply() {
claims, err := requireBackend(&c.Controller)
if err != nil {
jsonErr(&c.Controller, 401, 401, err.Error())
return
}
raw, err := io.ReadAll(c.Ctx.Request.Body)
if err != nil {
jsonErr(&c.Controller, 400, 400, "参数错误")
return
}
var p struct {
Tid uint64 `json:"tid"`
SubDomain string `json:"sub_domain"`
MainDomain string `json:"main_domain"`
}
@@ -404,10 +493,11 @@ func (c *BackendTenantDomainController) Apply() {
jsonErr(&c.Controller, 400, 400, "参数错误")
return
}
if p.Tid == 0 {
jsonErr(&c.Controller, 400, 400, "租户ID不能为空")
if claims.TenantId <= 0 {
jsonErr(&c.Controller, 400, 400, "当前登录账号未归属租户")
return
}
tenantID := uint64(claims.TenantId)
sub := strings.TrimSpace(p.SubDomain)
main := strings.TrimSpace(p.MainDomain)
if sub == "" {
@@ -423,13 +513,14 @@ func (c *BackendTenantDomainController) Apply() {
return
}
// 该租户是否已有域名
// 该租户是否已申请过平台二级域名(自有域名单独计数,两者可以共存)
cnt, _ := models.Orm.QueryTable(new(models.SystemTenantDomain)).
Filter("tid", p.Tid).
Filter("tid", tenantID).
Filter("domain_type", models.DomainTypePlatformSub).
Filter("delete_time__isnull", true).
Count()
if cnt > 0 {
jsonErr(&c.Controller, 400, 400, "该租户已有域名,请删除后再次申请")
jsonErr(&c.Controller, 400, 400, "该租户已有二级域名,请删除后再次申请")
return
}
@@ -457,9 +548,10 @@ func (c *BackendTenantDomainController) Apply() {
full := sub + "." + main
now := time.Now()
tid := p.Tid
tid := tenantID
row := &models.SystemTenantDomain{
Tid: &tid,
DomainType: models.DomainTypePlatformSub,
SubDomain: &sub,
MainDomain: &main,
FullDomain: &full,
@@ -500,6 +592,10 @@ func (c *BackendTenantDomainController) Audit() {
jsonErr(&c.Controller, 404, 404, "域名不存在")
return
}
if row.DomainType == models.DomainTypeCustom {
jsonErr(&c.Controller, 400, 400, "自有域名无需人工审核,解析检测与证书签发通过后自动生效")
return
}
if row.Status != 0 {
jsonErr(&c.Controller, 400, 400, "该域名已审核过了")
return
@@ -547,11 +643,20 @@ func (c *BackendTenantDomainController) ToggleStatus() {
return
}
if row.Status == 0 {
jsonErr(&c.Controller, 400, 400, "审核中不可操作")
if row.DomainType == models.DomainTypeCustom {
jsonErr(&c.Controller, 400, 400, "该域名尚未生效(解析或证书未就绪),无需禁用")
} else {
jsonErr(&c.Controller, 400, 400, "审核中不可操作")
}
return
}
newStatus := 2
if row.Status == 2 {
// 自有域名重新启用要求证书还在,否则启用后访问会报证书错误
if row.DomainType == models.DomainTypeCustom && row.SSLStatus != models.SSLStatusIssued {
jsonErr(&c.Controller, 400, 400, "该域名的 HTTPS 证书未就绪,请先检测解析并等待证书签发")
return
}
newStatus = 1
}
now := time.Now()
@@ -568,8 +673,10 @@ func (c *BackendTenantDomainController) ToggleStatus() {
}
// Delete DELETE /backend/domain/tenant/delete/:id
// 只能删除本租户的域名记录。
func (c *BackendTenantDomainController) Delete() {
if _, err := requireBackend(&c.Controller); err != nil {
claims, err := requireBackend(&c.Controller)
if err != nil {
jsonErr(&c.Controller, 401, 401, err.Error())
return
}
@@ -579,11 +686,14 @@ func (c *BackendTenantDomainController) Delete() {
jsonErr(&c.Controller, 400, 400, "参数错误")
return
}
now := time.Now()
n, err := models.Orm.QueryTable(new(models.SystemTenantDomain)).
qs := models.Orm.QueryTable(new(models.SystemTenantDomain)).
Filter("id", id).
Filter("delete_time__isnull", true).
Update(map[string]interface{}{"delete_time": now, "update_time": now})
Filter("delete_time__isnull", true)
if claims.TenantId > 0 {
qs = qs.Filter("tid", claims.TenantId)
}
now := time.Now()
n, err := qs.Update(map[string]interface{}{"delete_time": now, "update_time": now})
if err != nil {
jsonErr(&c.Controller, 500, 500, "删除失败: "+err.Error())
return