mirror of
https://github.com/MirageNetwork/MirageServer.git
synced 2026-04-22 15:47:14 +08:00
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
+2
-6
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+49
-11
@@ -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").
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -439,7 +439,7 @@
|
||||
|
||||
<p>你是否要将名为 <strong>{{.Hostname}}</strong> 的设备接入进 <strong>{{.Netname}}</strong> 蜃境网络?
|
||||
|
||||
这将使该设备基于ACL控制规则可能访问到你蜃境网络中的其他资源。
|
||||
这将使该设备基于ACL控制规则可能访问到该蜃境网络中的其他资源。
|
||||
</p>
|
||||
</header>
|
||||
|
||||
|
||||
+4
-9
@@ -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
|
||||
}
|
||||
|
||||
+548
-235
File diff suppressed because it is too large
Load Diff
+754
-614
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,176 @@
|
||||
<script setup>
|
||||
import { ref, watch, computed } from "vue";
|
||||
import { useDisScroll } from "/src/utils.js";
|
||||
|
||||
const emit = defineEmits(["refresh-offers", "update-tcd"]);
|
||||
|
||||
useDisScroll();
|
||||
|
||||
const props = defineProps({
|
||||
currenttcd: String,
|
||||
tcdoffers: Array,
|
||||
});
|
||||
const wantedTCD = ref("");
|
||||
|
||||
function getTCDOffers() {
|
||||
wantedTCD.value = "";
|
||||
axios
|
||||
.get("/admin/api/tcd/offers")
|
||||
.then(function (response) {
|
||||
if (response.data["status"] == "success") {
|
||||
emit("refresh-offers", response.data["data"]["tcds"]);
|
||||
} else {
|
||||
console.log(response.data["status"]);
|
||||
}
|
||||
})
|
||||
.catch(function (error) {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
||||
function setWantedTCD(newWantedTCD) {
|
||||
wantedTCD.value = newWantedTCD;
|
||||
console.log("new Wanted TCD: " + wantedTCD.value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
@click.self="$emit('close')"
|
||||
class="fixed overflow-y-auto inset-0 py-8 z-30 bg-gray-900 bg-opacity-[0.07]"
|
||||
style="pointer-events: auto"
|
||||
>
|
||||
<div
|
||||
class="bg-white rounded-lg relative p-4 md:p-6 text-gray-700 max-w-lg min-w-[19rem] my-8 mx-auto w-[97%] shadow-2xl"
|
||||
tabindex="-1"
|
||||
style="pointer-events: auto"
|
||||
>
|
||||
<header class="flex items-center justify-between space-x-4 mb-5 mr-8">
|
||||
<div class="font-semibold text-lg truncate">重命名 {{ currenttcd }}</div>
|
||||
</header>
|
||||
<form @submit.prevent="">
|
||||
<p v-if="tcdoffers.length == 0" class="mb-6">
|
||||
重命名你的蜃境可能会破坏你蜃境网络中已有的连接
|
||||
</p>
|
||||
<footer v-if="tcdoffers.length == 0" class="flex mt-10 justify-end space-x-4">
|
||||
<button
|
||||
@click="getTCDOffers"
|
||||
class="btn border-0 bg-blue-500 hover:bg-blue-900 disabled:bg-blue-500/60 text-white disabled:text-white/60 h-9 min-h-fit w-full"
|
||||
>
|
||||
我明白,让我重命名蜃境
|
||||
</button>
|
||||
</footer>
|
||||
|
||||
<div v-if="tcdoffers.length > 0" class="mt-8 mb-8">
|
||||
<p class="mb-5">
|
||||
选择一个生成的名称或者摇一摇获得新的一组。如果你选择了新的名称,你之前的名称会被释放给其他租户使用。
|
||||
</p>
|
||||
<div class="mb-5">
|
||||
<label v-for="(tcdOffer, i) in tcdoffers" class="flex mt-1">
|
||||
<input
|
||||
:checked="tcdOffer.tcd == wantedTCD"
|
||||
@change="setWantedTCD(tcdOffer.tcd)"
|
||||
type="radio"
|
||||
name="tcd-rad"
|
||||
class="radio radio-xs mt-1 mr-1"
|
||||
:value="tcdOffer.tcd"
|
||||
/>{{ tcdOffer.tcd.split(".")[0]
|
||||
}}<span class="text-gray-400">{{
|
||||
tcdOffer.tcd.replace(tcdOffer.tcd.split(".")[0], "")
|
||||
}}</span>
|
||||
</label>
|
||||
</div>
|
||||
<button
|
||||
@click="getTCDOffers"
|
||||
class="btn border border-stone-300 hover:border-stone-300 bg-stone-200 hover:bg-stone-300 text-black h-9 min-h-fit"
|
||||
type="button"
|
||||
>
|
||||
<span>
|
||||
<svg
|
||||
width="1.25rem"
|
||||
height="1.25rem"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="text-gray-500"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M11.555 2.34556L11.5559 2.34511C11.694 2.27669 11.846 2.24109 12 2.24109C12.1541 2.24109 12.3061 2.27669 12.4442 2.34511L12.445 2.34556L19.759 6.00252L12 9.88198L4.24113 6.00252L11.555 2.34556ZM3.00005 7.61805V16.767C2.99875 16.9534 3.04954 17.1364 3.14672 17.2954C3.24374 17.4542 3.38313 17.5827 3.5492 17.6666L11 21.392V11.618L3.00005 7.61805ZM20.4428 17.6656L13 21.387V11.6181L21 7.61804V16.7695C20.9999 16.9555 20.948 17.1379 20.8499 17.296C20.7519 17.4541 20.6117 17.5817 20.445 17.6645L20.4428 17.6656ZM13.335 0.554497L12.89 1.45003L13.3373 0.555601L21.335 4.5545L21.3363 4.55513C21.8356 4.80352 22.2557 5.18614 22.5496 5.66006C22.8438 6.13439 22.9998 6.6819 23 7.24003V16.7706C22.9998 17.3287 22.8438 17.8757 22.5496 18.35C22.2557 18.824 21.8354 19.2067 21.336 19.4551L21.335 19.4556L13.3373 23.4545C12.9206 23.6629 12.461 23.7715 11.995 23.7715C11.529 23.7715 11.0693 23.6629 10.6525 23.4543L2.65283 19.4545L2.65007 19.4531C2.15077 19.2015 1.7317 18.8154 1.44015 18.3383C1.14927 17.8623 0.996878 17.3147 1.00005 16.7569L1.00005 7.2395C1.00034 6.68137 1.15633 6.13439 1.45047 5.66006C1.74436 5.18613 2.16454 4.80349 2.66381 4.55511L2.66505 4.5545L10.6628 0.555601L10.665 0.554497C11.0799 0.348361 11.5368 0.241089 12 0.241089C12.4633 0.241089 12.9202 0.348361 13.335 0.554497ZM12 7C12.8284 7 13.5 6.55228 13.5 6C13.5 5.44772 12.8284 5 12 5C11.1716 5 10.5 5.44772 10.5 6C10.5 6.55228 11.1716 7 12 7ZM5 14C5.55228 14 6 14.6716 6 15.5C6 16.3284 5.55228 17 5 17C4.44772 17 4 16.3284 4 15.5C4 14.6716 4.44772 14 5 14ZM10 13.5C10 12.6716 9.55228 12 9 12C8.44771 12 8 12.6716 8 13.5C8 14.3284 8.44771 15 9 15C9.55229 15 10 14.3284 10 13.5ZM15 12C15.5523 12 16 12.6716 16 13.5C16 14.3284 15.5523 15 15 15C14.4477 15 14 14.3284 14 13.5C14 12.6716 14.4477 12 15 12ZM16 17.5C16 16.6716 15.5523 16 15 16C14.4477 16 14 16.6716 14 17.5C14 18.3284 14.4477 19 15 19C15.5523 19 16 18.3284 16 17.5ZM19 14C19.5523 14 20 14.6716 20 15.5C20 16.3284 19.5523 17 19 17C18.4477 17 18 16.3284 18 15.5C18 14.6716 18.4477 14 19 14ZM20 11.5C20 10.6716 19.5523 10 19 10C18.4477 10 18 10.6716 18 11.5C18 12.3284 18.4477 13 19 13C19.5523 13 20 12.3284 20 11.5Z"
|
||||
></path>
|
||||
</svg>
|
||||
</span>
|
||||
<span class="flex-1 ml-3">摇一摇</span>
|
||||
</button>
|
||||
</div>
|
||||
<footer v-if="tcdoffers.length > 0" class="flex mt-10 justify-end space-x-4">
|
||||
<button
|
||||
@click="$emit('close')"
|
||||
class="btn border border-stone-300 hover:border-stone-300 bg-stone-200 hover:bg-stone-300 text-black h-9 min-h-fit"
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button
|
||||
@click="$emit('update-tcd', wantedTCD)"
|
||||
:disabled="wantedTCD == ''"
|
||||
class="btn border-0 bg-blue-500 hover:bg-blue-900 disabled:bg-blue-500/60 text-white disabled:text-white/60 h-9 min-h-fit"
|
||||
>
|
||||
保存
|
||||
</button>
|
||||
</footer>
|
||||
</form>
|
||||
<button
|
||||
@click="$emit('close')"
|
||||
class="btn btn-sm btn-ghost absolute top-5 right-5 px-2 py-2 border-0 bg-base-0 focus:bg-base-200 hover:bg-base-200"
|
||||
type="button"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1.25em"
|
||||
height="1.25em"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<line x1="18" y1="6" x2="6" y2="18"></line>
|
||||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.toggle {
|
||||
border: 0;
|
||||
--tglbg: #d6d3d1;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.toggle:checked {
|
||||
border: 0;
|
||||
--tglbg: #1e40af;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.toggle:disabled {
|
||||
--togglehandleborder: 0 0 0 3px white inset,
|
||||
var(--handleoffsetcalculator) 0 0 3px white inset;
|
||||
}
|
||||
|
||||
.radio {
|
||||
--chkbg: white;
|
||||
border-width: 2px;
|
||||
border-color: #d6d3d1;
|
||||
}
|
||||
.radio:checked {
|
||||
--chkbg: white;
|
||||
border-width: 5px;
|
||||
border-color: #3e5db3;
|
||||
}
|
||||
</style>
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
Reference in New Issue
Block a user