store.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. package store
  2. import (
  3. "crypto/md5"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "github.com/jsmartx/giter/util"
  8. fs "io/ioutil"
  9. "net/url"
  10. "os"
  11. "path/filepath"
  12. "strings"
  13. )
  14. const ROOT = "~/.giter/"
  15. type User struct {
  16. Name string `json:"name"`
  17. Email string `json:"email"`
  18. Scheme string `json:"scheme"`
  19. Host string `json:"host"`
  20. Port string `json:"port"`
  21. }
  22. type Options struct {
  23. KeyPath string
  24. Password string
  25. }
  26. func (u *User) Hash() string {
  27. host := util.JoinHostPort(u.Host, u.Port)
  28. url := fmt.Sprintf("%s://%s@%s", u.Scheme, u.Name, host)
  29. hash := md5.New()
  30. hash.Write([]byte(url))
  31. return fmt.Sprintf("%x", hash.Sum(nil))
  32. }
  33. func (u *User) Url(pwd string) string {
  34. fullUrl := &url.URL{
  35. Scheme: u.Scheme,
  36. User: url.UserPassword(u.Name, pwd),
  37. Host: util.JoinHostPort(u.Host, u.Port),
  38. }
  39. return fullUrl.String()
  40. }
  41. func (u *User) KeyPath() (string, error) {
  42. return util.JoinPath(ROOT, "keys", u.Hash())
  43. }
  44. func (u *User) FullHost() string {
  45. host := util.JoinHostPort(u.Host, u.Port)
  46. return fmt.Sprintf("%s://%s", u.Scheme, host)
  47. }
  48. func (u *User) IsSSH() bool {
  49. if u.Scheme == "http" || u.Scheme == "https" {
  50. return false
  51. }
  52. return true
  53. }
  54. func (u *User) String() string {
  55. return fmt.Sprintf("%s - %s", u.Name, u.FullHost())
  56. }
  57. type Config struct {
  58. Users []*User `json:"users"`
  59. }
  60. type Store struct {
  61. c *Config
  62. }
  63. func loadConfig() *Config {
  64. cfgPath, err := util.JoinPath(ROOT, "config.json")
  65. if err != nil {
  66. return nil
  67. }
  68. f, err := os.Open(cfgPath)
  69. defer f.Close()
  70. if err != nil {
  71. return nil
  72. }
  73. var cfg Config
  74. parser := json.NewDecoder(f)
  75. parser.Decode(&cfg)
  76. return &cfg
  77. }
  78. func saveConfig(cfg *Config) error {
  79. root, err := util.Mkdir(ROOT)
  80. if err != nil {
  81. return err
  82. }
  83. b, err := json.MarshalIndent(cfg, "", " ")
  84. if err != nil {
  85. return err
  86. }
  87. cfgPath := filepath.Join(root, "config.json")
  88. // write marshaled data to the file
  89. return fs.WriteFile(cfgPath, b, 0755)
  90. }
  91. func New() *Store {
  92. cfg := loadConfig()
  93. if cfg != nil {
  94. return &Store{c: cfg}
  95. }
  96. cfg = &Config{
  97. Users: make([]*User, 0),
  98. }
  99. saveConfig(cfg)
  100. return &Store{c: cfg}
  101. }
  102. func (s *Store) check(user *User) error {
  103. for _, u := range s.c.Users {
  104. if u.Hash() == user.Hash() {
  105. return errors.New("User already exist!")
  106. }
  107. }
  108. return nil
  109. }
  110. func (s *Store) Add(u *User, opts *Options) error {
  111. if err := s.check(u); err != nil {
  112. return err
  113. }
  114. keysDir, err := util.Mkdir(ROOT, "keys")
  115. if err != nil {
  116. return err
  117. }
  118. if u.IsSSH() {
  119. pvtPath := filepath.Join(keysDir, u.Hash())
  120. pubPath := filepath.Join(keysDir, u.Hash()+".pub")
  121. if opts.KeyPath == "" {
  122. if err := util.Keygen(pubPath, pvtPath, 4096); err != nil {
  123. return err
  124. }
  125. } else {
  126. if err := util.Copy(opts.KeyPath, pvtPath); err != nil {
  127. return err
  128. }
  129. if err := util.Copy(opts.KeyPath+".pub", pubPath); err != nil {
  130. return err
  131. }
  132. }
  133. } else {
  134. pwdPath := filepath.Join(keysDir, u.Hash()+".credential")
  135. data := []byte(u.Url(opts.Password))
  136. if err := fs.WriteFile(pwdPath, data, 0755); err != nil {
  137. return err
  138. }
  139. }
  140. s.c.Users = append(s.c.Users, u)
  141. return saveConfig(s.c)
  142. }
  143. func (s *Store) Update(hash string, u *User, opts *Options) error {
  144. if hash != u.Hash() {
  145. if err := s.check(u); err != nil {
  146. return err
  147. }
  148. }
  149. keysDir, err := util.Mkdir(ROOT, "keys")
  150. if err != nil {
  151. return err
  152. }
  153. if u.IsSSH() {
  154. pvtPath := filepath.Join(keysDir, u.Hash())
  155. pubPath := filepath.Join(keysDir, u.Hash()+".pub")
  156. if opts.KeyPath == "" {
  157. if err := util.Keygen(pubPath, pvtPath, 4096); err != nil {
  158. return err
  159. }
  160. } else if opts.KeyPath != pvtPath {
  161. if err := util.Copy(opts.KeyPath, pvtPath); err != nil {
  162. return err
  163. }
  164. if err := util.Copy(opts.KeyPath+".pub", pubPath); err != nil {
  165. return err
  166. }
  167. }
  168. } else {
  169. pwdPath := filepath.Join(keysDir, u.Hash()+".credential")
  170. if opts.Password != "" {
  171. data := []byte(u.Url(opts.Password))
  172. if err := fs.WriteFile(pwdPath, data, 0755); err != nil {
  173. return err
  174. }
  175. } else if hash != u.Hash() {
  176. oldPath := filepath.Join(keysDir, hash+".credential")
  177. if err := util.Copy(oldPath, pwdPath); err != nil {
  178. return err
  179. }
  180. }
  181. }
  182. for i := 0; i < len(s.c.Users); i++ {
  183. if s.c.Users[i].Hash() == hash {
  184. s.c.Users[i] = u
  185. }
  186. }
  187. return saveConfig(s.c)
  188. }
  189. func (s *Store) Delete(u *User) error {
  190. p, err := u.KeyPath()
  191. if err != nil {
  192. return err
  193. }
  194. if u.IsSSH() {
  195. os.Remove(p)
  196. os.Remove(p + ".pub")
  197. } else {
  198. os.Remove(p + ".credential")
  199. }
  200. users := make([]*User, 0)
  201. for _, v := range s.c.Users {
  202. if v.Hash() != u.Hash() {
  203. users = append(users, v)
  204. }
  205. }
  206. s.c.Users = users
  207. return saveConfig(s.c)
  208. }
  209. func (s *Store) List(filter string, strict bool) []*User {
  210. users := make([]*User, 0)
  211. for _, u := range s.c.Users {
  212. if !strict && strings.Contains(u.Name, filter) {
  213. users = append(users, u)
  214. }
  215. if strict && u.Name == filter {
  216. users = append(users, u)
  217. }
  218. }
  219. return users
  220. }