util.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. package onedrive
  2. import (
  3. "errors"
  4. "fmt"
  5. "net/url"
  6. "strings"
  7. "github.com/IceWhaleTech/CasaOS-Common/utils/logger"
  8. "github.com/IceWhaleTech/CasaOS/drivers/base"
  9. "go.uber.org/zap"
  10. )
  11. var (
  12. client_id = "private build"
  13. client_secret = "private build"
  14. )
  15. var onedriveHostMap = map[string]Host{
  16. "global": {
  17. Oauth: "https://login.microsoftonline.com",
  18. Api: "https://graph.microsoft.com",
  19. },
  20. "cn": {
  21. Oauth: "https://login.chinacloudapi.cn",
  22. Api: "https://microsoftgraph.chinacloudapi.cn",
  23. },
  24. "us": {
  25. Oauth: "https://login.microsoftonline.us",
  26. Api: "https://graph.microsoft.us",
  27. },
  28. "de": {
  29. Oauth: "https://login.microsoftonline.de",
  30. Api: "https://graph.microsoft.de",
  31. },
  32. }
  33. func EncodePath(path string, all ...bool) string {
  34. seg := strings.Split(path, "/")
  35. toReplace := []struct {
  36. Src string
  37. Dst string
  38. }{
  39. {Src: "%", Dst: "%25"},
  40. {"%", "%25"},
  41. {"?", "%3F"},
  42. {"#", "%23"},
  43. }
  44. for i := range seg {
  45. if len(all) > 0 && all[0] {
  46. seg[i] = url.PathEscape(seg[i])
  47. } else {
  48. for j := range toReplace {
  49. seg[i] = strings.ReplaceAll(seg[i], toReplace[j].Src, toReplace[j].Dst)
  50. }
  51. }
  52. }
  53. return strings.Join(seg, "/")
  54. }
  55. func (d *Onedrive) GetMetaUrl(auth bool, path string) string {
  56. host := onedriveHostMap[d.Region]
  57. path = EncodePath(path, true)
  58. if auth {
  59. return host.Oauth
  60. }
  61. if d.IsSharepoint {
  62. if path == "/" || path == "\\" {
  63. return fmt.Sprintf("%s/v1.0/sites/%s/drive/root", host.Api, d.SiteId)
  64. } else {
  65. return fmt.Sprintf("%s/v1.0/sites/%s/drive/root:%s:", host.Api, d.SiteId, path)
  66. }
  67. } else {
  68. if path == "/" || path == "\\" {
  69. return fmt.Sprintf("%s/v1.0/me/drive/root", host.Api)
  70. } else {
  71. return fmt.Sprintf("%s/v1.0/me/drive/root:%s:", host.Api, path)
  72. }
  73. }
  74. }
  75. func (d *Onedrive) refreshToken() error {
  76. var err error
  77. for i := 0; i < 3; i++ {
  78. err = d._refreshToken()
  79. if err == nil {
  80. break
  81. }
  82. }
  83. return err
  84. }
  85. func (d *Onedrive) getRefreshToken() error {
  86. url := d.GetMetaUrl(true, "") + "/common/oauth2/v2.0/token"
  87. var resp base.TokenResp
  88. var e TokenErr
  89. res, err := base.RestyClient.R().SetResult(&resp).SetError(&e).SetFormData(map[string]string{
  90. "grant_type": "authorization_code",
  91. "client_id": d.ClientID,
  92. "client_secret": d.ClientSecret,
  93. "code": d.Code,
  94. "redirect_uri": d.RedirectUri,
  95. }).Post(url)
  96. if err != nil {
  97. return err
  98. }
  99. logger.Info("get refresh token", zap.String("res", res.String()))
  100. if e.Error != "" {
  101. return fmt.Errorf("%s", e.ErrorDescription)
  102. }
  103. if resp.RefreshToken == "" {
  104. return errors.New("refresh token is empty")
  105. }
  106. d.RefreshToken, d.AccessToken = resp.RefreshToken, resp.AccessToken
  107. return nil
  108. }
  109. func (d *Onedrive) _refreshToken() error {
  110. url := d.GetMetaUrl(true, "") + "/common/oauth2/v2.0/token"
  111. var resp base.TokenResp
  112. var e TokenErr
  113. res, err := base.RestyClient.R().SetResult(&resp).SetError(&e).SetFormData(map[string]string{
  114. "grant_type": "refresh_token",
  115. "client_id": d.ClientID,
  116. "client_secret": d.ClientSecret,
  117. "redirect_uri": d.RedirectUri,
  118. "refresh_token": d.RefreshToken,
  119. }).Post(url)
  120. if err != nil {
  121. return err
  122. }
  123. logger.Info("get refresh token", zap.String("res", res.String()))
  124. if e.Error != "" {
  125. return fmt.Errorf("%s", e.ErrorDescription)
  126. }
  127. if resp.RefreshToken == "" {
  128. return errors.New("refresh token is empty")
  129. }
  130. d.RefreshToken, d.AccessToken = resp.RefreshToken, resp.AccessToken
  131. return nil
  132. }
  133. func (d *Onedrive) Request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
  134. req := base.RestyClient.R()
  135. req.SetHeader("Authorization", "Bearer "+d.AccessToken)
  136. if callback != nil {
  137. callback(req)
  138. }
  139. if resp != nil {
  140. req.SetResult(resp)
  141. }
  142. var e RespErr
  143. req.SetError(&e)
  144. res, err := req.Execute(method, url)
  145. if err != nil {
  146. return nil, err
  147. }
  148. if e.Error.Code != "" {
  149. if e.Error.Code == "InvalidAuthenticationToken" {
  150. err = d.refreshToken()
  151. if err != nil {
  152. return nil, err
  153. }
  154. return d.Request(url, method, callback, resp)
  155. }
  156. return nil, errors.New(e.Error.Message)
  157. }
  158. return res.Body(), nil
  159. }
  160. func GetConfig() Onedrive {
  161. config := Onedrive{}
  162. config.ClientID = client_id
  163. config.ClientSecret = client_secret
  164. config.RootFolderID = "/"
  165. config.AuthUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=" + client_id + "&response_type=code&redirect_uri=https%3A%2F%2Fcloudoauth.files.casaos.app&scope=offline_access+files.readwrite.all&state=${HOST}%2Fv1%2Frecover%2FOnedrive"
  166. config.Icon = "./img/driver/OneDrive.svg"
  167. config.Region = "global"
  168. config.RedirectUri = "https://cloudoauth.files.casaos.app"
  169. return config
  170. }