修改:user uniqIndex name-> name+orgName导致的联合唯一键使用

This commit is contained in:
chuanh
2023-03-15 10:09:50 +00:00
parent 23aed99581
commit 58c8f44eee
11 changed files with 162 additions and 149 deletions

View File

@@ -173,7 +173,7 @@ func (h *Mirage) expireEphemeralNodesWorker() {
}
for _, user := range users {
machines, err := h.ListMachinesByUser(user.Name)
machines, err := h.ListMachinesByUser(user.ID)
if err != nil {
log.Error().
Err(err).
@@ -218,7 +218,7 @@ func (h *Mirage) expireExpiredMachinesWorker() {
}
for _, user := range users {
machines, err := h.ListMachinesByUser(user.Name)
machines, err := h.ListMachinesByUser(user.ID)
if err != nil {
log.Error().
Err(err).

View File

@@ -179,7 +179,7 @@ func (h *Mirage) ConsoleSelfAPI(
func (h *Mirage) verifyTokenIDandGetUser(
writer http.ResponseWriter,
req *http.Request,
) string {
) *User {
controlCodeCookie, err := req.Cookie("miragecontrol")
if err == http.ErrNoCookie {
errRes := adminTemplateConfig{ErrorMsg: "Token不存在"}
@@ -190,7 +190,7 @@ func (h *Mirage) verifyTokenIDandGetUser(
Err(err).
Msg("Failed to write response")
}
return ""
return nil
}
controlCode := controlCodeCookie.Value
controlCodeC, concontrolCodeExpiration, ok := h.controlCodeCache.GetWithExpiration(controlCode)
@@ -203,7 +203,7 @@ func (h *Mirage) verifyTokenIDandGetUser(
Err(err).
Msg("Failed to write response")
}
return ""
return nil
}
controlCodeItem := controlCodeC.(ControlCacheItem)
user, err := h.GetUserByID(controlCodeItem.uid)
@@ -216,10 +216,9 @@ func (h *Mirage) verifyTokenIDandGetUser(
Err(err).
Msg("Failed to write response")
}
return ""
return nil
}
userName := user.Name
return userName
return user
}
// 控制台获取设备信息列表的API
@@ -266,9 +265,8 @@ func (h *Mirage) ConsoleMachinesAPI(
}
return
}
userName := user.Name
UserMachines, err := h.ListMachinesByUser(userName)
UserMachines, err := h.ListMachinesByUser(user.ID)
if err != nil {
errRes := adminTemplateConfig{ErrorMsg: "查询用户节点列表失败"}
err = json.NewEncoder(writer).Encode(&errRes)
@@ -450,16 +448,11 @@ func (h *Mirage) getNetSettingAPI(
writer http.ResponseWriter,
req *http.Request,
) {
userName := h.verifyTokenIDandGetUser(writer, req)
if userName == "" {
user := h.verifyTokenIDandGetUser(writer, req)
if user.CheckEmpty() {
h.doAPIResponse(writer, "用户信息核对失败", nil)
return
}
user, err := h.GetUser(userName)
if err != nil {
h.doAPIResponse(writer, "查询用户失败:"+err.Error(), nil)
return
}
netsettingData := NetSettingResData{
FileSharing: false, //未实现
ServicesCollection: false, //未实现
@@ -478,8 +471,8 @@ func (h *Mirage) ConsoleUpdateKeyExpiryAPI(
writer http.ResponseWriter,
req *http.Request,
) {
userName := h.verifyTokenIDandGetUser(writer, req)
if userName == "" {
user := h.verifyTokenIDandGetUser(writer, req)
if user.CheckEmpty() {
h.doAPIResponse(writer, "用户信息核对失败", nil)
return
}
@@ -496,7 +489,7 @@ func (h *Mirage) ConsoleUpdateKeyExpiryAPI(
h.doAPIResponse(writer, "从请求获取新值失败:"+err.Error(), nil)
return
}
err = h.UpdateUserKeyExpiry(userName, uint(newExpiryDuration))
err = h.UpdateUserKeyExpiry(user.Name, user.OrgName, uint(newExpiryDuration))
if err != nil {
h.doAPIResponse(writer, "更新密钥过期时长失败:"+err.Error(), nil)
return
@@ -508,8 +501,8 @@ func (h *Mirage) ConsoleMachinesUpdateAPI(
writer http.ResponseWriter,
req *http.Request,
) {
userName := h.verifyTokenIDandGetUser(writer, req)
if userName == "" {
user := h.verifyTokenIDandGetUser(writer, req)
if user.CheckEmpty() {
h.doAPIResponse(writer, "用户信息核对失败", nil)
return
}
@@ -535,7 +528,7 @@ func (h *Mirage) ConsoleMachinesUpdateAPI(
h.doAPIResponse(writer, "查询用户设备失败", nil)
return
}
if toUpdateMachine.User.Name != userName {
if toUpdateMachine.User.ID != user.ID {
h.doAPIResponse(writer, "用户没有该权限", nil)
return
}
@@ -669,9 +662,9 @@ func (h *Mirage) ConsoleRemoveMachineAPI(
writer http.ResponseWriter,
req *http.Request,
) {
userName := h.verifyTokenIDandGetUser(writer, req)
user := h.verifyTokenIDandGetUser(writer, req)
resData := removeMachineRes{}
if userName == "" {
if user.CheckEmpty() {
resData.Status = "Error"
resData.ErrMsg = "用户信息核对失败"
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
@@ -685,7 +678,7 @@ func (h *Mirage) ConsoleRemoveMachineAPI(
}
return
}
UserMachines, err := h.ListMachinesByUser(userName)
UserMachines, err := h.ListMachinesByUser(user.ID)
if err != nil {
resData.Status = "Error"
resData.ErrMsg = "用户设备检索失败"

View File

@@ -23,16 +23,11 @@ func (h *Mirage) CAPIGetDNS(
w http.ResponseWriter,
r *http.Request,
) {
userName := h.verifyTokenIDandGetUser(w, r)
if userName == "" {
user := h.verifyTokenIDandGetUser(w, r)
if user.CheckEmpty() {
h.doAPIResponse(w, "用户信息核对失败", nil)
return
}
user, err := h.GetUser(userName)
if err != nil {
h.doAPIResponse(w, "查询用户失败:"+err.Error(), nil)
return
}
userDNSCfg, userBaseDomain := user.GetDNSConfig(h.cfg.IPPrefixes)
dnsData := DNSData{
Domains: make([]string, 0),
@@ -78,8 +73,8 @@ func (h *Mirage) CAPIPostDNS(
w http.ResponseWriter,
r *http.Request,
) {
userName := h.verifyTokenIDandGetUser(w, r)
if userName == "" {
user := h.verifyTokenIDandGetUser(w, r)
if user.CheckEmpty() {
h.doAPIResponse(w, "用户信息核对失败", nil)
return
}
@@ -90,7 +85,7 @@ func (h *Mirage) CAPIPostDNS(
}
reqData := DNSData{}
json.NewDecoder(r.Body).Decode(&reqData)
err = h.UpdateDNSConfig(userName, reqData)
err = h.UpdateDNSConfig(user, reqData)
if err != nil {
h.doAPIResponse(w, "更新用户DNS设置失败", nil)
return
@@ -104,9 +99,13 @@ func (h *Mirage) CAPIDelDNS(
w http.ResponseWriter,
r *http.Request,
) {
userName := h.verifyTokenIDandGetUser(w, r)
user := h.verifyTokenIDandGetUser(w, r)
if user.CheckEmpty() {
h.doAPIResponse(w, "用户信息核对失败", nil)
return
}
targetKeyID := strings.TrimPrefix(r.URL.Path, "/admin/api/keys/")
allKeys, err := h.ListPreAuthKeys(userName)
allKeys, err := h.ListPreAuthKeys(user.ID)
if err != nil {
h.doAPIResponse(w, "查询用户密钥信息失败", nil)
return

View File

@@ -57,13 +57,13 @@ func (h *Mirage) CAPIGetKeys(
w http.ResponseWriter,
r *http.Request,
) {
userName := h.verifyTokenIDandGetUser(w, r)
if userName == "" {
user := h.verifyTokenIDandGetUser(w, r)
if user.CheckEmpty() {
h.doAPIResponse(w, "用户信息核对失败", nil)
return
}
authKeys, err := h.ListPreAuthKeys(userName)
authKeys, err := h.ListPreAuthKeys(user.ID)
if err != nil {
h.doAPIResponse(w, "授权密钥查询失败", nil)
return
@@ -109,8 +109,8 @@ func (h *Mirage) CAPIPostKeys(
w http.ResponseWriter,
r *http.Request,
) {
userName := h.verifyTokenIDandGetUser(w, r)
if userName == "" {
user := h.verifyTokenIDandGetUser(w, r)
if user.CheckEmpty() {
h.doAPIResponse(w, "用户信息核对失败", nil)
return
}
@@ -125,7 +125,7 @@ func (h *Mirage) CAPIPostKeys(
case "authkey":
keyCfg := reqData.KeyData.Authkey
keyExpiration := time.Now().Add(time.Duration(reqData.KeyData.ExpirySeconds) * time.Second)
genedAuthKey, err := h.CreatePreAuthKey(userName, keyCfg.Reusable, keyCfg.Ephemeral, &keyExpiration, keyCfg.Tags)
genedAuthKey, err := h.CreatePreAuthKey(user, keyCfg.Reusable, keyCfg.Ephemeral, &keyExpiration, keyCfg.Tags)
if err != nil {
h.doAPIResponse(w, "授权密钥创建失败", nil)
return
@@ -145,9 +145,13 @@ func (h *Mirage) CAPIDelKeys(
w http.ResponseWriter,
r *http.Request,
) {
userName := h.verifyTokenIDandGetUser(w, r)
user := h.verifyTokenIDandGetUser(w, r)
if user.CheckEmpty() {
h.doAPIResponse(w, "用户信息核对失败", nil)
return
}
targetKeyID := strings.TrimPrefix(r.URL.Path, "/admin/api/keys/")
allKeys, err := h.ListPreAuthKeys(userName)
allKeys, err := h.ListPreAuthKeys(user.ID)
if err != nil {
h.doAPIResponse(w, "查询用户密钥信息失败", nil)
return

View File

@@ -20,8 +20,8 @@ func (h *Mirage) CAPIGetTags(
w http.ResponseWriter,
r *http.Request,
) {
userName := h.verifyTokenIDandGetUser(w, r)
if userName == "" {
user := h.verifyTokenIDandGetUser(w, r)
if user.CheckEmpty() {
h.doAPIResponse(w, "用户信息核对失败", nil)
return
}
@@ -51,8 +51,8 @@ func (h *Mirage) CAPIPostTags(
w http.ResponseWriter,
r *http.Request,
) {
userName := h.verifyTokenIDandGetUser(w, r)
if userName == "" {
user := h.verifyTokenIDandGetUser(w, r)
if user.CheckEmpty() {
h.doAPIResponse(w, "用户信息核对失败", nil)
return
}
@@ -96,8 +96,8 @@ func (h *Mirage) CAPIDelTags(
w http.ResponseWriter,
r *http.Request,
) {
userName := h.verifyTokenIDandGetUser(w, r)
if userName == "" {
user := h.verifyTokenIDandGetUser(w, r)
if user.CheckEmpty() {
h.doAPIResponse(w, "用户信息核对失败", nil)
return
}

View File

@@ -465,8 +465,8 @@ func (h *Mirage) GetMachinesInPrefix(ip netip.Prefix) []Machine {
// cgao6
// GetMachine finds a Machine by user and backendlogid and returns the Machine struct.
func (h *Mirage) GetMachineNSBLID(user string, backendlogid string) (*Machine, error) {
machines, err := h.ListMachinesByUser(user)
func (h *Mirage) GetMachineNSBLID(userID int64, backendlogid string) (*Machine, error) {
machines, err := h.ListMachinesByUser(userID)
if err != nil {
return nil, err
}
@@ -481,8 +481,8 @@ func (h *Mirage) GetMachineNSBLID(user string, backendlogid string) (*Machine, e
}
// GetMachine finds a Machine by name and user and returns the Machine struct.
func (h *Mirage) GetMachine(user string, name string) (*Machine, error) {
machines, err := h.ListMachinesByUser(user)
func (h *Mirage) GetMachine(userID int64, name string) (*Machine, error) {
machines, err := h.ListMachinesByUser(userID)
if err != nil {
return nil, err
}
@@ -497,8 +497,8 @@ func (h *Mirage) GetMachine(user string, name string) (*Machine, error) {
}
// GetMachineByGivenName finds a Machine by given name and user and returns the Machine struct.
func (h *Mirage) GetMachineByGivenName(user string, givenName string) (*Machine, error) {
machines, err := h.ListMachinesByUser(user)
func (h *Mirage) GetMachineByGivenName(userID int64, givenName string) (*Machine, error) {
machines, err := h.ListMachinesByUser(userID)
if err != nil {
return nil, err
}
@@ -622,7 +622,7 @@ func (h *Mirage) setAutoGenName(machine *Machine, newName string) (string, error
}
machine.GivenName = h.GenMachineName(machine.Hostname, machine.User.toTailscaleUser().ID, machine.MachineKey)
} else {
_, err := h.GetMachineByGivenName(machine.User.Name, newName)
_, err := h.GetMachineByGivenName(machine.User.ID, newName)
if err != nil && err != ErrMachineNotFound {
return "", fmt.Errorf("fail to check whether new name already exist: %w", err)
} else if err == nil {

View File

@@ -318,7 +318,7 @@ func (h *Mirage) findOrCreateNewUserForOIDCCallback(
userDisName string,
orgName string,
) (*User, error) {
user, err := h.GetUser(userName)
user, err := h.GetUser(userName, orgName)
if errors.Is(err, ErrUserNotFound) {
user, err = h.CreateUser(userName, userDisName, orgName)
if err != nil {

View File

@@ -16,6 +16,7 @@ const (
type Organization struct {
ID int64 `gorm:"primary_key;unique;not null"`
StableID string `gorm:"unique"`
Name string `gorm:"unique"`
ExpiryDuration uint `gorm:"default:180"`
EnableMagic bool `gorm:"default:false"`
@@ -36,6 +37,7 @@ func (o *Organization) BeforeCreate(tx *gorm.DB) error {
id := flakeID.Generate().Int64()
o.ID = id
}
o.StableID = GetShortId(o.ID)
return nil
}
@@ -67,6 +69,22 @@ func CreateOrgnaizationInTx(tx *gorm.DB, name string) (*Organization, error) {
return &org, nil
}
func (m *Mirage) GetOrgnaizationByName(name string) (*Organization, error) {
return GetOrgnaizationByNameInTx(m.db.Session(&gorm.Session{}), name)
}
func GetOrgnaizationByNameInTx(tx *gorm.DB, name string) (*Organization, error) {
var org Organization
if err := tx.Where("name = ?", name).Take(&org).Error; err != nil {
log.Error().
Str("func", "GetOrgnaizationByName").
Err(err).
Msg("Could not get row")
return nil, err
}
return &org, nil
}
func (m *Mirage) DestroyOrgnaization(orgId int64) error {
tx := m.db.Session(&gorm.Session{})
return DestroyOrgnaizationInTx(tx, orgId)

View File

@@ -43,16 +43,12 @@ type PreAuthKeyACLTag struct {
// CreatePreAuthKey creates a new PreAuthKey in a user, and returns it.
func (h *Mirage) CreatePreAuthKey(
userName string,
user *User,
reusable bool,
ephemeral bool,
expiration *time.Time,
aclTags []string,
) (*PreAuthKey, error) {
user, err := h.GetUser(userName)
if err != nil {
return nil, err
}
for _, tag := range aclTags {
if !strings.HasPrefix(tag, "tag:") {
@@ -108,14 +104,9 @@ func (h *Mirage) CreatePreAuthKey(
}
// ListPreAuthKeys returns the list of PreAuthKeys for a user.
func (h *Mirage) ListPreAuthKeys(userName string) ([]PreAuthKey, error) {
user, err := h.GetUser(userName)
if err != nil {
return nil, err
}
func (h *Mirage) ListPreAuthKeys(userID int64) ([]PreAuthKey, error) {
keys := []PreAuthKey{}
if err := h.db.Preload("User").Preload("ACLTags").Where(&PreAuthKey{UserID: user.ID}).Find(&keys).Error; err != nil {
if err := h.db.Preload("User").Preload("ACLTags").Where(&PreAuthKey{UserID: userID}).Find(&keys).Error; err != nil {
return nil, err
}

View File

@@ -42,7 +42,8 @@ var invalidCharsInUserRegex = regexp.MustCompile("[^a-z0-9-.]+")
type User struct {
ID int64 `gorm:"primary_key;unique;not null"`
StableID string `gorm:"unique"`
Name string `gorm:"unique"`
Name string `gorm:"uniqueIndex:idx_user_org"`
OrgName string `gorm:"uniqueIndex:idx_user_org"`
OrgId int64
Org Organization
Display_Name string `gorm:"unique"`
@@ -71,86 +72,78 @@ func (user *User) BeforeCreate(tx *gorm.DB) error {
id := flakeID.Generate().Int64()
user.ID = id
}
longid := user.ID
shortID := ""
for longid > 0 {
shortID = string("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"[longid%62]) + shortID
longid /= 62
}
user.StableID = shortID
user.StableID = GetShortId(user.ID)
return nil
}
func (user *User) CheckEmpty() bool {
return user == nil || user.ID == 0
}
// CreateUser creates a new User. Returns error if could not be created
// or another user already exists.
func (h *Mirage) CreateUser(name string, DisName string, OrgName string) (*User, error) {
func (h *Mirage) CreateUser(name string, disName string, orgName string) (*User, error) {
err := CheckForFQDNRules(name)
if err != nil {
return nil, err
}
user := User{}
if err := h.db.Where("name = ?", name).First(&user).Error; err == nil {
var count int64
err = h.db.Model(&User{}).Where("name = ?", name).Count(&count).Error
if err != nil {
return nil, err
}
if count > 0 {
return nil, ErrUserExists
}
user := User{}
user.Name = name
user.Display_Name = DisName
// 个人用户新建时候orgName为空
if len(OrgName) == 0 {
err := h.db.Transaction(func(tx *gorm.DB) error {
org, err := CreateOrgnaizationInTx(tx, name)
if err != nil {
return err
}
err = tx.Create(&user).Error
if err != nil {
log.Error().
Str("func", "CreateUser").
Err(err).
Msg("Could not create row")
return err
user.Display_Name = disName
err = h.db.Transaction(func(tx *gorm.DB) error {
var org *Organization
var trxErr error
//个人用户: userName 和 orgName相同
if user.Name == orgName {
org, trxErr = CreateOrgnaizationInTx(tx, orgName)
user.Role = RoleAdmin
} else {
//企业用户,需要先查询orgName是否存在
org, trxErr = GetOrgnaizationByNameInTx(tx, orgName)
if trxErr == nil && org.ID == 0 {
trxErr = ErrOrgNotFound
}
user.IsBelongToOrg = true
}
if trxErr == nil {
user.OrgId = org.ID
user.Org = *org
user.Role = RoleAdmin
return nil
})
if err != nil {
return nil, err
user.OrgName = org.Name
trxErr = tx.Create(&user).Error
}
return &user, nil
// 非个人用户新建,先查询组织id,再进行新建
} else {
org := Organization{}
err := h.db.Model(&Organization{}).Where("name = ?", OrgName).Take(&org).Error
if err == nil && org.ID == 0 {
err = ErrOrgNotFound
}
if err == nil {
user.IsBelongToOrg = true
err = h.db.Create(&user).Error
}
if err != nil {
if trxErr != nil {
log.Error().
Str("func", "CreateUser").
Err(err).
Err(trxErr).
Msg("Could not create row")
return nil, err
return trxErr
}
user.Org = org
return &user, nil
return nil
})
if err != nil {
return nil, err
}
return &user, nil
}
// DestroyUser destroys a User. Returns error if the User does
// not exist or if there are machines associated with it.
func (h *Mirage) DestroyUser(name string) error {
user, err := h.GetUser(name)
func (h *Mirage) DestroyUser(name, orgName string) error {
user, err := h.GetUser(name, orgName)
if err != nil {
return ErrUserNotFound
}
machines, err := h.ListMachinesByUser(name)
machines, err := h.ListMachinesByUser(user.ID)
if err != nil {
return err
}
@@ -158,7 +151,7 @@ func (h *Mirage) DestroyUser(name string) error {
return ErrUserStillHasNodes
}
keys, err := h.ListPreAuthKeys(name)
keys, err := h.ListPreAuthKeys(user.ID)
if err != nil {
return err
}
@@ -183,9 +176,9 @@ func (h *Mirage) DestroyUser(name string) error {
}
// Update User's node key expiry duration.
func (h *Mirage) UpdateUserKeyExpiry(name string, newDuration uint) error {
func (h *Mirage) UpdateUserKeyExpiry(name, orgName string, newDuration uint) error {
var err error
user, err := h.GetUser(name)
user, err := h.GetUser(name, orgName)
if err != nil {
return err
}
@@ -197,9 +190,9 @@ func (h *Mirage) UpdateUserKeyExpiry(name string, newDuration uint) error {
// RenameUser renames a User. Returns error if the User does
// not exist or if another User exists with the new name.
func (h *Mirage) RenameUser(oldName, newName string) error {
func (h *Mirage) RenameUser(oldName, newName string, orgName string) error {
var err error
oldUser, err := h.GetUser(oldName)
oldUser, err := h.GetUser(oldName, orgName)
if err != nil {
return err
}
@@ -207,7 +200,7 @@ func (h *Mirage) RenameUser(oldName, newName string) error {
if err != nil {
return err
}
_, err = h.GetUser(newName)
_, err = h.GetUser(newName, orgName)
if err == nil {
return ErrUserExists
}
@@ -238,6 +231,22 @@ func (h *Mirage) GetUserByID(id tailcfg.UserID) (*User, error) {
}
// GetUser fetches a user by name.
func (h *Mirage) GetUser(name, orgName string) (*User, error) {
user := User{}
if result := h.db.Preload("Org").First(&User{
Name: name,
OrgName: orgName,
}); errors.Is(
result.Error,
gorm.ErrRecordNotFound,
) {
return nil, ErrUserNotFound
}
return &user, nil
}
/*
func (h *Mirage) GetUser(name string) (*User, error) {
user := User{}
if result := h.db.Preload("Org").First(&user, "name = ?", name); errors.Is(
@@ -249,6 +258,7 @@ func (h *Mirage) GetUser(name string) (*User, error) {
return &user, nil
}
*/
// ListUsers gets all the existing users.
func (h *Mirage) ListUsers() ([]User, error) {
@@ -261,19 +271,13 @@ func (h *Mirage) ListUsers() ([]User, error) {
}
// ListMachinesByUser gets all the nodes in a given user.
func (h *Mirage) ListMachinesByUser(name string) ([]Machine, error) {
err := CheckForFQDNRules(name)
if err != nil {
return nil, err
func (h *Mirage) ListMachinesByUser(userID int64) ([]Machine, error) {
if userID == 0 {
return nil, ErrUserNotFound
}
user, err := h.GetUser(name)
if err != nil {
return nil, err
}
machines := []Machine{}
//TODO 是否需要组织信息
if err := h.db.Preload("AuthKey").Preload("AuthKey.User").Preload("User").Where(&Machine{UserID: user.ID}).Find(&machines).Error; err != nil {
if err := h.db.Preload("AuthKey").Preload("AuthKey.User").Preload("User").Where(&Machine{UserID: userID}).Find(&machines).Error; err != nil {
return nil, err
}
@@ -281,12 +285,12 @@ func (h *Mirage) ListMachinesByUser(name string) ([]Machine, error) {
}
// SetMachineUser assigns a Machine to a user.
func (h *Mirage) SetMachineUser(machine *Machine, username string) error {
func (h *Mirage) SetMachineUser(machine *Machine, username, orgName string) error {
err := CheckForFQDNRules(username)
if err != nil {
return err
}
user, err := h.GetUser(username)
user, err := h.GetUser(username, orgName)
if err != nil {
return err
}
@@ -524,13 +528,8 @@ func (me *User) GetDNSConfig(ipPrefixesCfg []netip.Prefix) (*tailcfg.DNSConfig,
return dnsConfig, baseDomain
}
func (h *Mirage) UpdateDNSConfig(userName string, newDNSCfg DNSData) error {
var err error
user, err := h.GetUser(userName)
if err != nil {
return err
}
if user.Org.ID == 0 {
func (h *Mirage) UpdateDNSConfig(user *User, newDNSCfg DNSData) error {
if user == nil || user.Org.ID == 0 {
return ErrOrgNotFound
}
org := &user.Org

View File

@@ -348,3 +348,12 @@ func AbsolutePathFromConfigPath(path string) string {
return path
}
func GetShortId(longID int64) string {
shortID := ""
for longID > 0 {
shortID = string("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"[longID%62]) + shortID
longID /= 62
}
return shortID
}