diff --git a/controller/app.go b/controller/app.go index 8efcd22..eddda5b 100644 --- a/controller/app.go +++ b/controller/app.go @@ -81,6 +81,8 @@ type Mirage struct { machineControlCodeCache *cache.Cache //organizationCache *cache.Cache + tcdCache *cache.Cache + longPollChanPool map[string]chan string ipAllocationMutex sync.Mutex @@ -124,6 +126,7 @@ func NewMirage(cfg *Config, db *gorm.DB) (*Mirage, error) { stateCodeCache: stateCodeCache, controlCodeCache: controlCodeCache, machineControlCodeCache: machineControlCodeCache, + tcdCache: cache.New(0, 0), longPollChanPool: longPollChanPool, smsCodeCache: smsCodeCache, shutdownChan: make(chan struct{}), @@ -317,6 +320,7 @@ func (h *Mirage) initRouter(router *mux.Router) { console_router.HandleFunc("/api/self", h.ConsoleSelfAPI).Methods(http.MethodGet) console_router.HandleFunc("/api/machines", h.ConsoleMachinesAPI).Methods(http.MethodGet) console_router.HandleFunc("/api/dns", h.CAPIGetDNS).Methods(http.MethodGet) + console_router.HandleFunc("/api/tcd/offers", h.CAPIGetTCDOffers).Methods(http.MethodGet) console_router.HandleFunc("/api/netsettings", h.getNetSettingAPI).Methods(http.MethodGet) console_router.HandleFunc("/api/keys", h.CAPIGetKeys).Methods(http.MethodGet) console_router.HandleFunc("/api/acls/tags", h.CAPIGetTags).Methods(http.MethodGet) @@ -328,6 +332,7 @@ func (h *Mirage) initRouter(router *mux.Router) { console_router.HandleFunc("/api/keys", h.CAPIPostKeys).Methods(http.MethodPost) console_router.HandleFunc("/api/acls/tags", h.CAPIPostTags).Methods(http.MethodPost) console_router.HandleFunc("/api/dns", h.CAPIPostDNS).Methods(http.MethodPost) + console_router.HandleFunc("/api/tcd", h.CAPIPostTCD).Methods(http.MethodPost) // DELETE(删除类)API console_router.PathPrefix("/api/keys/").HandlerFunc(h.CAPIDelKeys).Methods(http.MethodDelete) diff --git a/controller/config.go b/controller/config.go index 819befb..91f7a54 100644 --- a/controller/config.go +++ b/controller/config.go @@ -114,126 +114,3 @@ func (ac OIDCConfig) Value() (driver.Value, error) { bytes, err := json.Marshal(ac) return string(bytes), err } - -/* -type ACLConfig struct { - PolicyPath string -} - - -func LoadConfig() error { - viper.SetConfigName("config") - viper.AddConfigPath(".mirage") - - viper.SetEnvPrefix("mirage") - viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) - viper.AutomaticEnv() - - viper.SetDefault("oidc.scope", []string{oidc.ScopeOpenID, "profile", "email"}) - viper.SetDefault("oidc.strip_email_domain", true) - - if err := viper.ReadInConfig(); err != nil { - log.Warn().Err(err).Msg("Failed to read configuration from disk") - - return fmt.Errorf("fatal error reading config file: %w", err) - } - - return nil -} - -func GetACLConfig() ACLConfig { - policyPath := viper.GetString("acl_policy_path") - - return ACLConfig{ - PolicyPath: policyPath, - } -} - -func GetBasedomain() string { - var baseDomain string - if viper.IsSet("base_domain") { - baseDomain = viper.GetString("base_domain") - } else { - baseDomain = "sdp.net" // does not really matter when MagicDNS is not enabled - } - - return baseDomain -} - -func GetMirageConfig(srvAddr, serverURL string) (*Config, error) { - - baseDomain := GetBasedomain() - - configuredPrefixes := viper.GetStringSlice("ip_prefixes") - parsedPrefixes := make([]netip.Prefix, 0, len(configuredPrefixes)+1) - - for i, prefixInConfig := range configuredPrefixes { - prefix, err := netip.ParsePrefix(prefixInConfig) - if err != nil { - panic(fmt.Errorf("failed to parse ip_prefixes[%d]: %w", i, err)) - } - parsedPrefixes = append(parsedPrefixes, prefix) - } - - prefixes := make([]netip.Prefix, 0, len(parsedPrefixes)) - { - // dedup - normalizedPrefixes := make(map[string]int, len(parsedPrefixes)) - for i, p := range parsedPrefixes { - normalized, _ := netipx.RangeOfPrefix(p).Prefix() - normalizedPrefixes[normalized.String()] = i - } - - // convert back to list - for _, i := range normalizedPrefixes { - prefixes = append(prefixes, parsedPrefixes[i]) - } - } - - if len(prefixes) < 1 { - prefixes = append(prefixes, netip.MustParsePrefix("100.64.0.0/10")) - log.Warn(). - Msgf("'ip_prefixes' not configured, falling back to default: %v", prefixes) - } - - wxScanAdapterURL := viper.GetString("wxscan.url") - - return &Config{ - ServerURL: serverURL, - Addr: srvAddr, - IPPrefixes: prefixes, - - BaseDomain: baseDomain, - AllowRouteDueToMachine: viper.GetBool("allow_route_due_to_machine"), - - DERPURL: viper.GetString("derp_url"), - - ESURL: viper.GetString("es_url"), - ESKey: viper.GetString("es_apikey"), - - OIDC: OIDCConfig{ - Issuer: viper.GetString("ali.issuer"), - ClientID: viper.GetString("ali.client_id"), - ClientSecret: viper.GetString("ali.client_secret"), - Scope: viper.GetStringSlice("ali.scope"), - ExtraParams: viper.GetStringMapString("ali.extra_params"), - StripEmaildomain: viper.GetBool("ali.strip_email_domain"), - }, - wxScanURL: wxScanAdapterURL, - - IDaaS: ALIConfig{ - App: viper.GetString("idaas.app_id"), - ClientID: viper.GetString("idaas.cli_id"), - ClientKey: viper.GetString("idaas.cli_key"), - Instance: viper.GetString("idaas.instance"), - OrgID: viper.GetString("idaas.org_id"), - }, - SMS: SMSConfig{ - ID: viper.GetString("sms.access_id"), - Key: viper.GetString("sms.access_key"), - Sign: viper.GetString("sms.sms_sign"), - Template: viper.GetString("sms.sms_template"), - }, - }, nil -} -*/ diff --git a/controller/console.go b/controller/console.go index 09b77ce..f1faffa 100644 --- a/controller/console.go +++ b/controller/console.go @@ -155,7 +155,7 @@ func (h *Mirage) ConsoleSelfAPI( // 用户所属组织将存于数据库,合并实现前,不设置配置文件中该项,均按个人用户处理 renderData := adminTemplateConfig{ - Basedomain: h.cfg.BaseDomain, + Basedomain: user.Organization.MagicDnsDomain, UserNameHead: userNameHead, UserName: userDisName, UserAccount: userName, @@ -426,7 +426,7 @@ func (h *Mirage) ConsoleMachinesAPI( } renderData := adminTemplateConfig{ - Basedomain: h.cfg.BaseDomain, + Basedomain: user.Organization.MagicDnsDomain, MList: mlist, } diff --git a/controller/console_api_common.go b/controller/console_api_common.go index 0100a36..5814e2c 100644 --- a/controller/console_api_common.go +++ b/controller/console_api_common.go @@ -22,7 +22,7 @@ func (h *Mirage) ListIdps( // @data 响应数据:key值为data的json对象 func (h *Mirage) doAPIResponse(writer http.ResponseWriter, msg string, data interface{}) { res := APIResponse{} - if data != nil { + if msg == "" { res.Status = "success" res.Data = data } else { diff --git a/controller/console_api_dns.go b/controller/console_api_dns.go index 57f4701..ce583d4 100644 --- a/controller/console_api_dns.go +++ b/controller/console_api_dns.go @@ -5,6 +5,7 @@ import ( "encoding/json" "net/http" "strings" + "time" ) type DNSData struct { @@ -130,3 +131,86 @@ func (h *Mirage) CAPIDelDNS( } h.doAPIResponse(w, "", targetKeyID) } + +type TCDOffer struct { + TCD string `json:"tcd"` + Token string `json:"token"` +} + +type TCDOffers struct { + TCDs []TCDOffer `json:"tcds"` +} + +// 接受/admin/api/tcd/offers的Get请求,用于获取新一轮随机TCD +func (h *Mirage) CAPIGetTCDOffers( + w http.ResponseWriter, + r *http.Request, +) { + user := h.verifyTokenIDandGetUser(w, r) + if user.CheckEmpty() { + h.doAPIResponse(w, "用户信息核对失败", nil) + return + } + if oldTCDOffers, ok := h.tcdCache.Get(user.Organization.StableID); ok { + for _, tcd := range oldTCDOffers.([]TCDOffer) { + h.tcdCache.Delete(tcd.TCD) + } + } + newTCDOffers := TCDOffers{ + TCDs: make([]TCDOffer, 0), + } + count := 4 + for count > 0 { + tmpTCD, err := h.GenNewMagicDNSDomain(h.db) + if err != nil { + continue + } + tmpTCDToken := h.GenStateCode() + if _, ok := h.tcdCache.Get(tmpTCD); ok { + continue + } + h.tcdCache.Set(tmpTCD, tmpTCDToken, 24*time.Hour) + newTCDOffers.TCDs = append(newTCDOffers.TCDs, TCDOffer{ + TCD: tmpTCD, + Token: tmpTCDToken, + }) + count-- + } + h.tcdCache.Set(user.Organization.StableID, newTCDOffers.TCDs, 24*time.Hour) + h.doAPIResponse(w, "", newTCDOffers) +} + +// 接受/admin/api/tcd的Post请求,用于更新TCD +func (h *Mirage) CAPIPostTCD( + w http.ResponseWriter, + r *http.Request, +) { + user := h.verifyTokenIDandGetUser(w, r) + if user.CheckEmpty() { + h.doAPIResponse(w, "用户信息核对失败", nil) + return + } + err := r.ParseForm() + if err != nil { + h.doAPIResponse(w, "用户请求解析失败:"+err.Error(), nil) + return + } + reqData := TCDOffer{} + json.NewDecoder(r.Body).Decode(&reqData) + token, ok := h.tcdCache.Get(reqData.TCD) + if !ok || token != reqData.Token { + h.doAPIResponse(w, "请求的新蜃境网域名称有误", nil) + return + } + err = h.UpdateMagicDNSDomain(user.OrganizationID, reqData.TCD) + if err != nil { + h.doAPIResponse(w, "更新蜃境网域名称失败", nil) + return + } + if oldTCDOffers, ok := h.tcdCache.Get(user.Organization.StableID); ok { + for _, tcd := range oldTCDOffers.([]TCDOffer) { + h.tcdCache.Delete(tcd.TCD) + } + } + h.doAPIResponse(w, "", nil) +} diff --git a/controller/console_auth.go b/controller/console_auth.go index a5e8993..0ca8ffa 100644 --- a/controller/console_auth.go +++ b/controller/console_auth.go @@ -309,7 +309,7 @@ func (h *Mirage) deviceRegPortal( //未绑定用户,显示connectDevice user, _ := h.GetUserByID(controlCodeItem.uid) Hostname := aCodeItem.regReq.Hostinfo.Hostname - Netname := user.Name + Netname := user.Organization.Name Nodekey := aCodeItem.regReq.NodeKey.String() OS := aCodeItem.regReq.Hostinfo.OS + "(" + aCodeItem.regReq.Hostinfo.OSVersion + ")" ClientVer := aCodeItem.regReq.Hostinfo.IPNVersion @@ -329,7 +329,7 @@ func (h *Mirage) deviceRegPortal( return } Hostname := machine.GivenName - Netname := machine.User.Name + Netname := machine.User.Organization.Name MIP := machine.IPAddresses[0].String() if len(machine.IPAddresses) > 1 && machine.IPAddresses[1].Is4() { MIP = machine.IPAddresses[1].String() @@ -399,7 +399,7 @@ func (h *Mirage) deviceReg( return } Hostname := machine.GivenName - Netname := machine.User.Name + Netname := machine.User.Organization.Name MIP := machine.IPAddresses[0].String() if len(machine.IPAddresses) > 1 && machine.IPAddresses[1].Is4() { MIP = machine.IPAddresses[1].String() @@ -419,7 +419,7 @@ func (h *Mirage) deviceReg( h.longPollChanPool[aCode] <- "ok" // longpoll的救赎 Hostname := machine.GivenName - Netname := machine.User.Name + Netname := machine.User.Organization.Name MIP := machine.IPAddresses[0].String() if len(machine.IPAddresses) > 1 && machine.IPAddresses[1].Is4() { MIP = machine.IPAddresses[1].String() diff --git a/controller/dns.go b/controller/dns.go index 7828c38..253559c 100644 --- a/controller/dns.go +++ b/controller/dns.go @@ -194,11 +194,7 @@ func getMapResponseDNSConfig( // Only inject the Search Domain of the current user - shared nodes should use their full FQDN dnsConfig.Domains = append( dnsConfig.Domains, - fmt.Sprintf( - "%s.%s", - machine.User.Name, - baseDomain, - ), + baseDomain, ) /* cgao6: due to we need to add uncomparable field to User, can't use mapset any more @@ -214,7 +210,7 @@ func getMapResponseDNSConfig( userSet = append(userSet, p.User) } for _, user := range userSet { //cgao6: same as above .ToSlice() { - dnsRoute := fmt.Sprintf("%v.%v", user.Name, baseDomain) + dnsRoute := user.Organization.MagicDnsDomain dnsConfig.Routes[dnsRoute] = nil } } /* else { diff --git a/controller/machine.go b/controller/machine.go index 690a348..abf5b50 100644 --- a/controller/machine.go +++ b/controller/machine.go @@ -965,9 +965,8 @@ func (h *Mirage) toNode( if machine.User.Organization.EnableMagic { //[cgao6 removed] dnsConfig != nil && dnsConfig.Proxied { // MagicDNS _, baseDomain := machine.User.GetDNSConfig(h.cfg.IPPrefixes) hostname = fmt.Sprintf( - "%s.%s.%s", + "%s.%s", machine.GivenName, - machine.User.Name, baseDomain, ) if len(hostname) > maxHostnameLength { diff --git a/controller/organization.go b/controller/organization.go index 7f3397d..f92d5d8 100644 --- a/controller/organization.go +++ b/controller/organization.go @@ -2,10 +2,12 @@ package controller import ( "errors" + "strings" "time" "github.com/bwmarrin/snowflake" "github.com/rs/zerolog/log" + "github.com/sethvargo/go-diceware/diceware" "gorm.io/gorm" "tailscale.com/tailcfg" ) @@ -22,11 +24,11 @@ type Organization struct { ID int64 `gorm:"primary_key;unique;not null"` StableID string `gorm:"unique"` Name string `gorm:"uniqueIndex:idx_name_provider"` - DisplayName string Provider string `gorm:"uniqueIndex:idx_name_provider"` ExpiryDuration uint `gorm:"default:180"` EnableMagic bool `gorm:"default:false"` - OverrideLocal bool `gorm:"default:false"` + MagicDnsDomain string + OverrideLocal bool `gorm:"default:false"` Nameservers StringList SplitDns SplitDNS AclPolicy *ACLPolicy @@ -50,18 +52,43 @@ func (o *Organization) BeforeCreate(tx *gorm.DB) error { return nil } -func (m *Mirage) CreateOrgnaization(name, displayName, provider string) (*Organization, error) { - tx := m.db.Session(&gorm.Session{}) - org, err := CreateOrgnaizationInTx(tx, name, displayName, provider) +func (m *Mirage) GenNewMagicDNSDomain(tx *gorm.DB) (string, error) { + list, err := diceware.Generate(2) if err != nil { - return nil, err + log.Error().Err(err).Msg("Could not generate passphrase") + return "", err } - m.UpdateACLRulesOfOrg(org) - return org, nil + tmpMagicDNSDomain := strings.Join(list, "-") + "." + m.cfg.BaseDomain + for { + if errors.Is(tx.First(&Organization{}, "magic_dns_domain = ?", tmpMagicDNSDomain).Error, gorm.ErrRecordNotFound) { + break + } + list, err = diceware.Generate(2) + if err != nil { + log.Error().Err(err).Msg("Could not generate passphrase") + return "", err + } + tmpMagicDNSDomain = strings.Join(list, "-") + "." + m.cfg.BaseDomain + } + return tmpMagicDNSDomain, nil } -func CreateOrgnaizationInTx(tx *gorm.DB, name, displayName, provider string) (*Organization, error) { - if len(name) == 0 || len(displayName) == 0 || len(provider) == 0 { +func (m *Mirage) UpdateMagicDNSDomain(orgID int64, netMagicDomain string) error { + org, err := m.GetOrgnaizationByID(orgID) + if err != nil { + return err + } + org.MagicDnsDomain = netMagicDomain + err = m.db.Save(org).Error + if err != nil { + return err + } + m.setLastStateChangeToNow() + return nil +} + +func (m *Mirage) CreateOrgnaizationInTx(tx *gorm.DB, name, provider string) (*Organization, error) { + if len(name) == 0 || len(provider) == 0 { return nil, ErrCreateOrgParams } var count int64 @@ -73,7 +100,6 @@ func CreateOrgnaizationInTx(tx *gorm.DB, name, displayName, provider string) (*O } org := Organization{} org.Name = name - org.DisplayName = displayName org.Provider = provider org.AclPolicy = &ACLPolicy{ ACLs: []ACL{{ @@ -84,6 +110,18 @@ func CreateOrgnaizationInTx(tx *gorm.DB, name, displayName, provider string) (*O }}, } org.ExpiryDuration = DefaultExpireTime + + //cgao6: 添加组织幻域域名roll生成 + newMagicDNSDomain, err := m.GenNewMagicDNSDomain(tx) + if err != nil { + log.Error(). + Str("func", "CreateOrgnaization"). + Err(err). + Msg("Could not generate magic dns domain") + return nil, err + } + org.MagicDnsDomain = newMagicDNSDomain + if err := tx.Create(&org).Error; err != nil { log.Error(). Str("func", "CreateOrgnaization"). diff --git a/controller/protocol_common_utils.go b/controller/protocol_common_utils.go index 7b73804..febd551 100644 --- a/controller/protocol_common_utils.go +++ b/controller/protocol_common_utils.go @@ -104,7 +104,7 @@ func (h *Mirage) generateMapResponse( DNSConfig: dnsConfig, // TODO: Only send if updated - Domain: h.cfg.BaseDomain, + Domain: org.Name, // Do not instruct clients to collect services, we do not // support or do anything with them diff --git a/controller/templates/connectDevice.html b/controller/templates/connectDevice.html index b0745ce..df60747 100644 --- a/controller/templates/connectDevice.html +++ b/controller/templates/connectDevice.html @@ -439,7 +439,7 @@

你是否要将名为 {{.Hostname}} 的设备接入进 {{.Netname}} 蜃境网络? - 这将使该设备基于ACL控制规则可能访问到你蜃境网络中的其他资源。 + 这将使该设备基于ACL控制规则可能访问到该蜃境网络中的其他资源。

diff --git a/controller/users.go b/controller/users.go index 8f222bc..74c5448 100644 --- a/controller/users.go +++ b/controller/users.go @@ -103,7 +103,7 @@ func (h *Mirage) CreateUser(name string, disName string, orgName string, provide org, trxErr = GetOrgnaizationByNameInTx(tx, orgName, provider) // 不存在: 创建组织 if errors.Is(trxErr, ErrOrgNotFound) { - org, trxErr = CreateOrgnaizationInTx(tx, orgName, orgName, provider) + org, trxErr = h.CreateOrgnaizationInTx(tx, orgName, provider) user.Role = RoleAdmin // 其他错误, 报错返回 } else if trxErr == nil && org.ID == 0 { @@ -332,7 +332,7 @@ func (n *User) toTailscaleUser() *tailcfg.User { LoginName: n.Name, DisplayName: n.Display_Name, ProfilePicURL: "", - Domain: "headscale.net", + Domain: n.Organization.MagicDnsDomain, Logins: []tailcfg.LoginID{}, Created: time.Time{}, } @@ -346,7 +346,7 @@ func (n *User) toTailscaleLogin() *tailcfg.Login { LoginName: n.Name, DisplayName: n.Name, ProfilePicURL: "", - Domain: "headscale.net", + Domain: n.Organization.MagicDnsDomain, } return &login @@ -542,12 +542,7 @@ func (me *User) GetDNSConfig(ipPrefixesCfg []netip.Prefix) (*tailcfg.DNSConfig, } } - var baseDomain string - if viper.IsSet("base_domain") { - baseDomain = viper.GetString("base_domain") - } else { - baseDomain = "headscale.net" // does not really matter when MagicDNS is not enabled - } + baseDomain := me.Organization.MagicDnsDomain return dnsConfig, baseDomain } diff --git a/frontend/src/components/DNS.vue b/frontend/src/components/DNS.vue index 9b16a45..4bc1990 100644 --- a/frontend/src/components/DNS.vue +++ b/frontend/src/components/DNS.vue @@ -1,156 +1,165 @@ @@ -274,37 +315,68 @@ function removeNS() {
-

DNS

+

+ DNS +

蜃境网域

-

这个名称用来注册DNS条目、分享设备给其他蜃境网域以及发放TLS证书

+

+ 这个名称用来注册DNS条目、分享设备给其他蜃境网域以及发放TLS证书 +

-
- -
- +

幻域

-

自动为您蜃境网域中的设备注册域名,令您从而可以使用名称替代IP地址访问设备

+

+ 自动为您蜃境网域中的设备注册域名,令您从而可以使用名称替代IP地址访问设备 +

- - + +
@@ -317,84 +389,159 @@ function removeNS() {

{{ MNetName }}

- + class="inline-flex items-center align-middle justify-center font-medium border border-gray-200 bg-gray-200 text-gray-600 rounded-sm px-1 text-xs relative -top-px" + > + + fill="currentColor" + > + fill="currentColor" + > 幻域
-
-
+
+
100.100.100.100
-
+
+ -
+ +
-
-
-
+
+
+
+ - -
+ d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" + > + +

{{ singleDomain }}

-
+
+
- + class="inline-flex items-center align-middle justify-center font-medium border border-gray-200 bg-gray-200 text-gray-600 rounded-sm px-1 text-xs" + > + - - 分离 DNS + 分离 DNS
-
+
+
-
-
+
+
{{ oneRoute }}
-
@@ -404,13 +551,27 @@ function removeNS() {

全球域名服务器

- - +
-
-
+
+
本地 DNS 设置
-
+
+
-
+
{{ ns }}
-
- @@ -470,16 +685,28 @@ function removeNS() {
-

HTTPS 证书
- Unavailable
+

+ HTTPS 证书 +
+ Unavailable +

允许用户为他们的设备生辰HTTPS证书

- - + +
@@ -491,70 +718,156 @@ function removeNS() { - - + + + @@ -572,10 +885,10 @@ function removeNS() { } .toggle:disabled { - --togglehandleborder: 0 0 0 3px white inset, var(--handleoffsetcalculator) 0 0 3px white inset; + --togglehandleborder: 0 0 0 3px white inset, + var(--handleoffsetcalculator) 0 0 3px white inset; } - .tooltip { --tooltip-color: #faf9f8; --tooltip-text-color: #3a3939; diff --git a/frontend/src/components/Machine.vue b/frontend/src/components/Machine.vue index 3ad6714..d0f0530 100644 --- a/frontend/src/components/Machine.vue +++ b/frontend/src/components/Machine.vue @@ -17,79 +17,81 @@ const route = useRoute(); const toastShow = ref(false); const toastMsg = ref(""); watch(toastShow, () => { - if (toastShow.value) { - setTimeout(function () { toastShow.value = false }, 5000) - } -}) - -const hasSpecialStatus = computed(() => { - return ( - currentMachine.value["isEphemeral"] || - currentMachine.value["isExternal"] || - currentMachine.value["issharedout"] || - currentMachine.value["expirydesc"] == "已过期" || - currentMachine.value["neverExpires"] || - currentMachine.value["soonexpiry"] || - currentMachine.value["advertisedExitNode"] || - currentMachine.value["hasSubnets"] - ); + if (toastShow.value) { + setTimeout(function () { + toastShow.value = false; + }, 5000); + } }); -const activeBtn = ref(null) -const btnLeft = ref(0) -const btnTop = ref(0) -const machineMenuShow = ref(false) +const hasSpecialStatus = computed(() => { + return ( + currentMachine.value["isEphemeral"] || + currentMachine.value["isExternal"] || + currentMachine.value["issharedout"] || + currentMachine.value["expirydesc"] == "已过期" || + currentMachine.value["neverExpires"] || + currentMachine.value["soonexpiry"] || + currentMachine.value["advertisedExitNode"] || + currentMachine.value["hasSubnets"] + ); +}); + +const activeBtn = ref(null); +const btnLeft = ref(0); +const btnTop = ref(0); +const machineMenuShow = ref(false); function watchWindowChange() { + if (activeBtn.value != null) { + btnLeft.value = activeBtn.value?.getBoundingClientRect().left + 78; + btnTop.value = activeBtn.value?.getBoundingClientRect().top + 20; + } + window.onresize = () => { if (activeBtn.value != null) { - btnLeft.value = activeBtn.value?.getBoundingClientRect().left + 78 - btnTop.value = activeBtn.value?.getBoundingClientRect().top + 20 + btnLeft.value = activeBtn.value?.getBoundingClientRect().left + 78; + btnTop.value = activeBtn.value?.getBoundingClientRect().top + 20; } - window.onresize = () => { - if (activeBtn.value != null) { - btnLeft.value = activeBtn.value?.getBoundingClientRect().left + 78 - btnTop.value = activeBtn.value?.getBoundingClientRect().top + 20 - } - } - window.onscroll = () => { - if (activeBtn.value != null) { - btnLeft.value = activeBtn.value?.getBoundingClientRect().left + 78 - btnTop.value = activeBtn.value?.getBoundingClientRect().top + 20 - } + }; + window.onscroll = () => { + if (activeBtn.value != null) { + btnLeft.value = activeBtn.value?.getBoundingClientRect().left + 78; + btnTop.value = activeBtn.value?.getBoundingClientRect().top + 20; } + }; } function openMachineMenu(event) { - activeBtn.value = event.target - while (activeBtn.value?.tagName != "BUTTON" && activeBtn.value?.tagName != "button") { - activeBtn.value = activeBtn.value?.parentNode - } - btnLeft.value = activeBtn.value?.getBoundingClientRect().left + 78 - btnTop.value = activeBtn.value?.getBoundingClientRect().top + 20 - machineMenuShow.value = true; + activeBtn.value = event.target; + while (activeBtn.value?.tagName != "BUTTON" && activeBtn.value?.tagName != "button") { + activeBtn.value = activeBtn.value?.parentNode; + } + btnLeft.value = activeBtn.value?.getBoundingClientRect().left + 78; + btnTop.value = activeBtn.value?.getBoundingClientRect().top + 20; + machineMenuShow.value = true; } function closeMachineMenu() { - activeBtn.value = null - machineMenuShow.value = false; + activeBtn.value = null; + machineMenuShow.value = false; } const delConfirmShow = ref(false); function showDelConfirm() { - closeMachineMenu(); - delConfirmShow.value = true; + closeMachineMenu(); + delConfirmShow.value = true; } const updateHostnameShow = ref(false); function showUpdateHostname() { - closeMachineMenu(); - updateHostnameShow.value = true; + closeMachineMenu(); + updateHostnameShow.value = true; } const setSubnetShow = ref(false); function showSetSubnet() { - closeMachineMenu(); - setSubnetShow.value = true; + closeMachineMenu(); + setSubnetShow.value = true; } const editTagsShow = ref(false); function showEditTags() { - closeMachineMenu(); - editTagsShow.value = true; + closeMachineMenu(); + editTagsShow.value = true; } //数据填充控制部分 @@ -99,600 +101,738 @@ const tagOwners = ref([]); const mipNotFound = ref(false); const basedomain = ref(""); onMounted(() => { - watchWindowChange() - axios - .get("/admin/api/machines") - .then(function (response) { + watchWindowChange(); + axios + .get("/admin/api/machines") + .then(function (response) { + if ( + response.data["needreauth"] != undefined || + response.data["needreauth"] == true + ) { + toastMsg.value = response.data["needreauthreason"] + ",登录状态失效,请重新登录"; + toastShow.value = true; + reject(); + } + // 处理成功情况 + if (response.data["errormsg"] == undefined || response.data["errormsg"] === "") { + basedomain.value = response.data["basedomain"]; + for (var k in response.data["mlist"]) { + if (response.data["mlist"][k]["addresses"][0] === route.params.mip) { + currentMachine.value = response.data["mlist"][k]; + currentMID.value = k; + let tailtwo = currentMachine.value["expirydesc"].slice(-2); + let tailthree = currentMachine.value["expirydesc"].slice(-3); if ( - response.data["needreauth"] != undefined || - response.data["needreauth"] == true + currentMachine.value["expirydesc"] == "马上就要过期" || + tailtwo == "分钟" || + tailtwo == "小时" || + tailthree == "剩1天" ) { - toastMsg.value = response.data["needreauthreason"] + ",登录状态失效,请重新登录"; - toastShow.value = true; - reject(); + currentMachine.value["soonexpiry"] = true; + } else { + currentMachine.value["soonexpiry"] = false; } - // 处理成功情况 - if (response.data["errormsg"] == undefined || response.data["errormsg"] === "") { - basedomain.value = response.data["basedomain"]; - for (var k in response.data["mlist"]) { - if (response.data["mlist"][k]["addresses"][0] === route.params.mip) { - currentMachine.value = response.data["mlist"][k]; - currentMID.value = k; - let tailtwo = currentMachine.value["expirydesc"].slice(-2); - let tailthree = currentMachine.value["expirydesc"].slice(-3); - if ( - currentMachine.value["expirydesc"] == "马上就要过期" || - tailtwo == "分钟" || - tailtwo == "小时" || - tailthree == "剩1天" - ) { - currentMachine.value["soonexpiry"] = true; - } else { - currentMachine.value["soonexpiry"] = false; - } - break; - } - } - if (currentMID.value == "") { - mipNotFound.value = true - } - } - }) - .catch(function (error) { - // 处理错误情况 - alert(error); - }) - .then(function () { - // 总是会执行 - }); - axios - .get("/admin/api/acls/tags") - .then(function (response) { - // 处理成功情况 - if (response.data["status"] == "success") { - tagOwners.value = response.data["data"]["tagOwners"] - } - }) - .catch(function (error) { - // 处理错误情况 - console.log(error); - }) + break; + } + } + if (currentMID.value == "") { + mipNotFound.value = true; + } + } + }) + .catch(function (error) { + // 处理错误情况 + alert(error); + }) + .then(function () { + // 总是会执行 + }); + axios + .get("/admin/api/acls/tags") + .then(function (response) { + // 处理成功情况 + if (response.data["status"] == "success") { + tagOwners.value = response.data["data"]["tagOwners"]; + } + }) + .catch(function (error) { + // 处理错误情况 + console.log(error); + }); }); //服务端请求 function setExpires() { - closeMachineMenu() - axios - .post("/admin/api/machines", { - mid: currentMID.value, - state: "set-expires" - }) - .then(function (response) { - if (response.data["status"] == "success") { - currentMachine.value["neverExpires"] = response.data["data"]["neverExpires"] - currentMachine.value["expirydesc"] = response.data["data"]["expires"] - if (response.data["data"]["neverExpires"] == true) { - toastMsg.value = "已禁用密钥过期"; - } else { - toastMsg.value = "已启用密钥过期"; - } - toastShow.value = true; - } else { - toastMsg.value = "失败:" + response.data["status"].substring(6); - toastShow.value = true; - } - }) - .catch(function (error) { - console.log(error); - }); + closeMachineMenu(); + axios + .post("/admin/api/machines", { + mid: currentMID.value, + state: "set-expires", + }) + .then(function (response) { + if (response.data["status"] == "success") { + currentMachine.value["neverExpires"] = response.data["data"]["neverExpires"]; + currentMachine.value["expirydesc"] = response.data["data"]["expires"]; + if (response.data["data"]["neverExpires"] == true) { + toastMsg.value = "已禁用密钥过期"; + } else { + toastMsg.value = "已启用密钥过期"; + } + toastShow.value = true; + } else { + toastMsg.value = "失败:" + response.data["status"].substring(6); + toastShow.value = true; + } + }) + .catch(function (error) { + console.log(error); + }); } function removeMachine() { - axios - .post("/admin/api/machine/remove", { - mid: currentMID, - }) - .then(function (response) { - if (response.data["status"] == "OK") { //TODO: 需处理设备页面删除跳转后的Toast显示 - delConfirmShow.value = false; - toastMsg.value = currentMachine.value["name"] + "已从您的蜃境网络移除!"; - toastShow.value = true; + axios + .post("/admin/api/machine/remove", { + mid: currentMID, + }) + .then(function (response) { + if (response.data["status"] == "OK") { + //TODO: 需处理设备页面删除跳转后的Toast显示 + delConfirmShow.value = false; + toastMsg.value = currentMachine.value["name"] + "已从您的蜃境网络移除!"; + toastShow.value = true; - router.push('/machines') - } else { - alert("失败:" + response.data["errmsg"]); - } - }) - .catch(function (error) { - console.log(error); - }); + router.push("/machines"); + } else { + alert("失败:" + response.data["errmsg"]); + } + }) + .catch(function (error) { + console.log(error); + }); } function hostnameUpdateDone(newName, newAutomaticNameMode, wantClose) { - currentMachine.value["name"] = newName - currentMachine.value["automaticNameMode"] = newAutomaticNameMode + currentMachine.value["name"] = newName; + currentMachine.value["automaticNameMode"] = newAutomaticNameMode; + nextTick(() => { + updateHostnameShow.value = !wantClose; nextTick(() => { - updateHostnameShow.value = !wantClose - nextTick(() => { - toastMsg.value = "已更新设备名称!" - toastShow.value = true - }) - }) + toastMsg.value = "已更新设备名称!"; + toastShow.value = true; + }); + }); } function hostnameUpdateFail(msg) { - toastMsg.value = "更新设备名称失败!" - toastShow.value = true + toastMsg.value = "更新设备名称失败!"; + toastShow.value = true; } function subnetUpdateDone(newAllIPs, newAllowedIPs, newExtraIPs, newEnExitNode) { - currentMachine.value["advertisedIPs"] = newAllIPs - currentMachine.value["allowedIPs"] = newAllowedIPs - currentMachine.value["extraIPs"] = newExtraIPs - currentMachine.value["allowedExitNode"] = newEnExitNode - nextTick(() => { - toastMsg.value = "已更新子网转发设置!" - toastShow.value = true - }) + currentMachine.value["advertisedIPs"] = newAllIPs; + currentMachine.value["allowedIPs"] = newAllowedIPs; + currentMachine.value["extraIPs"] = newExtraIPs; + currentMachine.value["allowedExitNode"] = newEnExitNode; + nextTick(() => { + toastMsg.value = "已更新子网转发设置!"; + toastShow.value = true; + }); } function subnetUpdateFail(msg) { - toastMsg.value = "更新子网转发设置失败!" - toastShow.value = true + toastMsg.value = "更新子网转发设置失败!"; + toastShow.value = true; } function tagsUpdateDone(mid, allowedTags, invalidTags) { - currentMachine.value["allowedTags"] = allowedTags - currentMachine.value["invalidTags"] = invalidTags - if (allowedTags.length > 0) { - currentMachine.value["hasTags"] = true - } - editTagsShow.value = false + currentMachine.value["allowedTags"] = allowedTags; + currentMachine.value["invalidTags"] = invalidTags; + if (allowedTags.length > 0) { + currentMachine.value["hasTags"] = true; + } + editTagsShow.value = false; + nextTick(() => { nextTick(() => { - nextTick(() => { - toastMsg.value = "已更新设备标签!" - toastShow.value = true - }) - }) + toastMsg.value = "已更新设备标签!"; + toastShow.value = true; + }); + }); } function tagsUpdateFail(msg) { - toastMsg.value = "更新设备标签失败!" + msg - toastShow.value = true + toastMsg.value = "更新设备标签失败!" + msg; + toastShow.value = true; } function isInvalidTag(tag) { - for (var i in tagOwners.value) { - if (tagOwners.value[i].tagName == tag) { - return false - } + for (var i in tagOwners.value) { + if (tagOwners.value[i].tagName == tag) { + return false; } - return true + } + return true; } diff --git a/frontend/src/components/dns/UpdateTCD.vue b/frontend/src/components/dns/UpdateTCD.vue new file mode 100644 index 0000000..6c54f26 --- /dev/null +++ b/frontend/src/components/dns/UpdateTCD.vue @@ -0,0 +1,176 @@ + + + + + diff --git a/go.mod b/go.mod index 64b39dc..da5e0f9 100644 --- a/go.mod +++ b/go.mod @@ -3,13 +3,13 @@ module MirageNetwork/MirageServer go 1.20 require ( - github.com/dexidp/dex v0.0.0-00010101000000-000000000000 github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.4 github.com/alibabacloud-go/dysmsapi-20170525/v3 v3.0.5 github.com/alibabacloud-go/eiam-developerapi-20220225/v2 v2.0.1 github.com/alibabacloud-go/tea v1.1.20 github.com/alibabacloud-go/tea-utils/v2 v2.0.1 github.com/coreos/go-oidc/v3 v3.5.0 + github.com/dexidp/dex v0.0.0-00010101000000-000000000000 github.com/glebarez/sqlite v1.7.0 github.com/gorilla/mux v1.8.0 github.com/klauspost/compress v1.16.3 @@ -27,7 +27,9 @@ require ( gorm.io/gorm v1.24.6 tailscale.com v1.38.1 ) + replace github.com/dexidp/dex => ./dex + require ( github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 // indirect github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68 // indirect @@ -83,6 +85,7 @@ require ( github.com/mdlayher/socket v0.4.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/sethvargo/go-diceware v0.3.0 github.com/spf13/afero v1.9.5 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect diff --git a/go.sum b/go.sum index bfcb9f7..5a0f36b 100644 --- a/go.sum +++ b/go.sum @@ -376,6 +376,8 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/samber/lo v1.37.0 h1:XjVcB8g6tgUp8rsPsJ2CvhClfImrpL04YpQHXeHPhRw= github.com/samber/lo v1.37.0/go.mod h1:9vaz2O4o8oOnK23pd2TrXufcbdbJIa3b6cstBWKpopA= +github.com/sethvargo/go-diceware v0.3.0 h1:UVVEfmN/uF50JfWAN7nbY6CiAlp5xeSx+5U0lWKkMCQ= +github.com/sethvargo/go-diceware v0.3.0/go.mod h1:lH5Q/oSPMivseNdhMERAC7Ti5oOPqsaVddU1BcN1CY0= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=