You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

568 lines
13 KiB

  1. package server
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "github.com/aws/aws-sdk-go/aws"
  6. "github.com/aws/aws-sdk-go/aws/awserr"
  7. "github.com/aws/aws-sdk-go/aws/session"
  8. "github.com/aws/aws-sdk-go/service/s3/s3manager"
  9. "io"
  10. "io/ioutil"
  11. "log"
  12. "mime"
  13. "net/http"
  14. "os"
  15. "path/filepath"
  16. "strings"
  17. "github.com/aws/aws-sdk-go/service/s3"
  18. "golang.org/x/net/context"
  19. "golang.org/x/oauth2"
  20. "golang.org/x/oauth2/google"
  21. "google.golang.org/api/drive/v3"
  22. "google.golang.org/api/googleapi"
  23. )
  24. type Storage interface {
  25. Get(token string, filename string) (reader io.ReadCloser, contentType string, contentLength uint64, err error)
  26. Head(token string, filename string) (contentType string, contentLength uint64, err error)
  27. Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error
  28. Delete(token string, filename string) error
  29. IsNotExist(err error) bool
  30. Type() string
  31. }
  32. type LocalStorage struct {
  33. Storage
  34. basedir string
  35. logger *log.Logger
  36. }
  37. func NewLocalStorage(basedir string, logger *log.Logger) (*LocalStorage, error) {
  38. return &LocalStorage{basedir: basedir, logger: logger}, nil
  39. }
  40. func (s *LocalStorage) Type() string {
  41. return "local"
  42. }
  43. func (s *LocalStorage) Head(token string, filename string) (contentType string, contentLength uint64, err error) {
  44. path := filepath.Join(s.basedir, token, filename)
  45. var fi os.FileInfo
  46. if fi, err = os.Lstat(path); err != nil {
  47. return
  48. }
  49. contentLength = uint64(fi.Size())
  50. contentType = mime.TypeByExtension(filepath.Ext(filename))
  51. return
  52. }
  53. func (s *LocalStorage) Get(token string, filename string) (reader io.ReadCloser, contentType string, contentLength uint64, err error) {
  54. path := filepath.Join(s.basedir, token, filename)
  55. // content type , content length
  56. if reader, err = os.Open(path); err != nil {
  57. return
  58. }
  59. var fi os.FileInfo
  60. if fi, err = os.Lstat(path); err != nil {
  61. return
  62. }
  63. contentLength = uint64(fi.Size())
  64. contentType = mime.TypeByExtension(filepath.Ext(filename))
  65. return
  66. }
  67. func (s *LocalStorage) Delete(token string, filename string) (err error) {
  68. metadata := filepath.Join(s.basedir, token, fmt.Sprintf("%s.metadata", filename))
  69. os.Remove(metadata)
  70. path := filepath.Join(s.basedir, token, filename)
  71. err = os.Remove(path)
  72. return
  73. }
  74. func (s *LocalStorage) IsNotExist(err error) bool {
  75. if err == nil {
  76. return false
  77. }
  78. return os.IsNotExist(err)
  79. }
  80. func (s *LocalStorage) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error {
  81. var f io.WriteCloser
  82. var err error
  83. path := filepath.Join(s.basedir, token)
  84. if err = os.Mkdir(path, 0700); err != nil && !os.IsExist(err) {
  85. return err
  86. }
  87. if f, err = os.OpenFile(filepath.Join(path, filename), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600); err != nil {
  88. return err
  89. }
  90. defer f.Close()
  91. if _, err = io.Copy(f, reader); err != nil {
  92. return err
  93. }
  94. return nil
  95. }
  96. type S3Storage struct {
  97. Storage
  98. bucket string
  99. session *session.Session
  100. s3 *s3.S3
  101. logger *log.Logger
  102. noMultipart bool
  103. }
  104. func NewS3Storage(accessKey, secretKey, bucketName, endpoint string, logger *log.Logger, disableMultipart bool) (*S3Storage, error) {
  105. sess := getAwsSession(accessKey, secretKey, endpoint)
  106. return &S3Storage{bucket: bucketName, s3: s3.New(sess), session: sess, logger: logger, noMultipart: disableMultipart}, nil
  107. }
  108. func (s *S3Storage) Type() string {
  109. return "s3"
  110. }
  111. func (s *S3Storage) Head(token string, filename string) (contentType string, contentLength uint64, err error) {
  112. key := fmt.Sprintf("%s/%s", token, filename)
  113. headRequest := &s3.HeadObjectInput{
  114. Bucket: aws.String(s.bucket),
  115. Key: aws.String(key),
  116. }
  117. // content type , content length
  118. _, response := s.s3.HeadObjectRequest(headRequest)
  119. if response.ContentType != nil {
  120. contentType = *response.ContentType
  121. }
  122. if response.ContentLength != nil {
  123. contentLength = uint64(*response.ContentLength)
  124. }
  125. return
  126. }
  127. func (s *S3Storage) IsNotExist(err error) bool {
  128. if err == nil {
  129. return false
  130. }
  131. if aerr, ok := err.(awserr.Error); ok {
  132. switch aerr.Code() {
  133. case s3.ErrCodeNoSuchKey:
  134. return true
  135. }
  136. }
  137. return false
  138. }
  139. func (s *S3Storage) Get(token string, filename string) (reader io.ReadCloser, contentType string, contentLength uint64, err error) {
  140. key := fmt.Sprintf("%s/%s", token, filename)
  141. getRequest := &s3.GetObjectInput{
  142. Bucket: aws.String(s.bucket),
  143. Key: aws.String(key),
  144. }
  145. _, response := s.s3.GetObjectRequest(getRequest)
  146. if response.ContentType != nil {
  147. contentType = *response.ContentType
  148. }
  149. if response.ContentLength != nil {
  150. contentLength = uint64(*response.ContentLength)
  151. }
  152. reader = response.Body
  153. return
  154. }
  155. func (s *S3Storage) Delete(token string, filename string) (err error) {
  156. metadata := fmt.Sprintf("%s/%s.metadata", token, filename)
  157. deleteRequest := &s3.DeleteObjectInput{
  158. Bucket: aws.String(s.bucket),
  159. Key: aws.String(metadata),
  160. }
  161. _, err = s.s3.DeleteObject(deleteRequest)
  162. if err != nil {
  163. return
  164. }
  165. key := fmt.Sprintf("%s/%s", token, filename)
  166. deleteRequest = &s3.DeleteObjectInput{
  167. Bucket: aws.String(s.bucket),
  168. Key: aws.String(key),
  169. }
  170. _, err = s.s3.DeleteObject(deleteRequest)
  171. return
  172. }
  173. func (s *S3Storage) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) (err error) {
  174. key := fmt.Sprintf("%s/%s", token, filename)
  175. s.logger.Printf("Uploading file %s to S3 Bucket", filename)
  176. var concurrency int
  177. if !s.noMultipart {
  178. concurrency = 20
  179. } else {
  180. concurrency = 1
  181. }
  182. // Create an uploader with the session and custom options
  183. uploader := s3manager.NewUploader(s.session, func(u *s3manager.Uploader) {
  184. u.PartSize = (1 << 20) * 5 // The minimum/default allowed part size is 5MB
  185. u.Concurrency = concurrency // default is 5
  186. u.MaxUploadParts = concurrency
  187. u.LeavePartsOnError = false
  188. })
  189. _, err = uploader.Upload(&s3manager.UploadInput{
  190. Bucket: aws.String(s.bucket),
  191. Key: aws.String(key),
  192. Body: reader,
  193. })
  194. return
  195. if err != nil {
  196. return
  197. }
  198. s.logger.Printf("Completed uploading %s", filename)
  199. return
  200. }
  201. type GDrive struct {
  202. service *drive.Service
  203. rootId string
  204. basedir string
  205. localConfigPath string
  206. chunkSize int
  207. logger *log.Logger
  208. }
  209. func NewGDriveStorage(clientJsonFilepath string, localConfigPath string, basedir string, chunkSize int, logger *log.Logger) (*GDrive, error) {
  210. b, err := ioutil.ReadFile(clientJsonFilepath)
  211. if err != nil {
  212. return nil, err
  213. }
  214. // If modifying these scopes, delete your previously saved client_secret.json.
  215. config, err := google.ConfigFromJSON(b, drive.DriveScope, drive.DriveMetadataScope)
  216. if err != nil {
  217. return nil, err
  218. }
  219. srv, err := drive.New(getGDriveClient(config, localConfigPath, logger))
  220. if err != nil {
  221. return nil, err
  222. }
  223. chunkSize = chunkSize * 1024 * 1024
  224. storage := &GDrive{service: srv, basedir: basedir, rootId: "", localConfigPath: localConfigPath, chunkSize: chunkSize, logger: logger}
  225. err = storage.setupRoot()
  226. if err != nil {
  227. return nil, err
  228. }
  229. return storage, nil
  230. }
  231. const GDriveRootConfigFile = "root_id.conf"
  232. const GDriveTokenJsonFile = "token.json"
  233. const GDriveDirectoryMimeType = "application/vnd.google-apps.folder"
  234. func (s *GDrive) setupRoot() error {
  235. rootFileConfig := filepath.Join(s.localConfigPath, GDriveRootConfigFile)
  236. rootId, err := ioutil.ReadFile(rootFileConfig)
  237. if err != nil && !os.IsNotExist(err) {
  238. return err
  239. }
  240. if string(rootId) != "" {
  241. s.rootId = string(rootId)
  242. return nil
  243. }
  244. dir := &drive.File{
  245. Name: s.basedir,
  246. MimeType: GDriveDirectoryMimeType,
  247. }
  248. di, err := s.service.Files.Create(dir).Fields("id").Do()
  249. if err != nil {
  250. return err
  251. }
  252. s.rootId = di.Id
  253. err = ioutil.WriteFile(rootFileConfig, []byte(s.rootId), os.FileMode(0600))
  254. if err != nil {
  255. return err
  256. }
  257. return nil
  258. }
  259. func (s *GDrive) hasChecksum(f *drive.File) bool {
  260. return f.Md5Checksum != ""
  261. }
  262. func (s *GDrive) list(nextPageToken string, q string) (*drive.FileList, error) {
  263. return s.service.Files.List().Fields("nextPageToken, files(id, name, mimeType)").Q(q).PageToken(nextPageToken).Do()
  264. }
  265. func (s *GDrive) findId(filename string, token string) (string, error) {
  266. filename = strings.Replace(filename, `'`, `\'`, -1)
  267. filename = strings.Replace(filename, `"`, `\"`, -1)
  268. fileId, tokenId, nextPageToken := "", "", ""
  269. q := fmt.Sprintf("'%s' in parents and name='%s' and mimeType='%s' and trashed=false", s.rootId, token, GDriveDirectoryMimeType)
  270. l, err := s.list(nextPageToken, q)
  271. if err != nil {
  272. return "", err
  273. }
  274. for 0 < len(l.Files) {
  275. for _, fi := range l.Files {
  276. tokenId = fi.Id
  277. break
  278. }
  279. if l.NextPageToken == "" {
  280. break
  281. }
  282. l, err = s.list(l.NextPageToken, q)
  283. }
  284. if filename == "" {
  285. return tokenId, nil
  286. } else if tokenId == "" {
  287. return "", fmt.Errorf("Cannot find file %s/%s", token, filename)
  288. }
  289. q = fmt.Sprintf("'%s' in parents and name='%s' and mimeType!='%s' and trashed=false", tokenId, filename, GDriveDirectoryMimeType)
  290. l, err = s.list(nextPageToken, q)
  291. if err != nil {
  292. return "", err
  293. }
  294. for 0 < len(l.Files) {
  295. for _, fi := range l.Files {
  296. fileId = fi.Id
  297. break
  298. }
  299. if l.NextPageToken == "" {
  300. break
  301. }
  302. l, err = s.list(l.NextPageToken, q)
  303. }
  304. if fileId == "" {
  305. return "", fmt.Errorf("Cannot find file %s/%s", token, filename)
  306. }
  307. return fileId, nil
  308. }
  309. func (s *GDrive) Type() string {
  310. return "gdrive"
  311. }
  312. func (s *GDrive) Head(token string, filename string) (contentType string, contentLength uint64, err error) {
  313. var fileId string
  314. fileId, err = s.findId(filename, token)
  315. if err != nil {
  316. return
  317. }
  318. var fi *drive.File
  319. if fi, err = s.service.Files.Get(fileId).Fields("mimeType", "size").Do(); err != nil {
  320. return
  321. }
  322. contentLength = uint64(fi.Size)
  323. contentType = fi.MimeType
  324. return
  325. }
  326. func (s *GDrive) Get(token string, filename string) (reader io.ReadCloser, contentType string, contentLength uint64, err error) {
  327. var fileId string
  328. fileId, err = s.findId(filename, token)
  329. if err != nil {
  330. return
  331. }
  332. var fi *drive.File
  333. fi, err = s.service.Files.Get(fileId).Fields("mimeType", "size", "md5Checksum").Do()
  334. if !s.hasChecksum(fi) {
  335. err = fmt.Errorf("Cannot find file %s/%s", token, filename)
  336. return
  337. }
  338. contentLength = uint64(fi.Size)
  339. contentType = fi.MimeType
  340. ctx := context.Background()
  341. var res *http.Response
  342. res, err = s.service.Files.Get(fileId).Context(ctx).Download()
  343. if err != nil {
  344. return
  345. }
  346. reader = res.Body
  347. return
  348. }
  349. func (s *GDrive) Delete(token string, filename string) (err error) {
  350. metadata, _ := s.findId(fmt.Sprintf("%s.metadata", filename), token)
  351. s.service.Files.Delete(metadata).Do()
  352. var fileId string
  353. fileId, err = s.findId(filename, token)
  354. if err != nil {
  355. return
  356. }
  357. err = s.service.Files.Delete(fileId).Do()
  358. return
  359. }
  360. func (s *GDrive) IsNotExist(err error) bool {
  361. if err != nil {
  362. if e, ok := err.(*googleapi.Error); ok {
  363. return e.Code == http.StatusNotFound
  364. }
  365. }
  366. return false
  367. }
  368. func (s *GDrive) Put(token string, filename string, reader io.Reader, contentType string, contentLength uint64) error {
  369. dirId, err := s.findId("", token)
  370. if err != nil {
  371. return err
  372. }
  373. if dirId == "" {
  374. dir := &drive.File{
  375. Name: token,
  376. Parents: []string{s.rootId},
  377. MimeType: GDriveDirectoryMimeType,
  378. }
  379. di, err := s.service.Files.Create(dir).Fields("id").Do()
  380. if err != nil {
  381. return err
  382. }
  383. dirId = di.Id
  384. }
  385. // Instantiate empty drive file
  386. dst := &drive.File{
  387. Name: filename,
  388. Parents: []string{dirId},
  389. MimeType: contentType,
  390. }
  391. ctx := context.Background()
  392. _, err = s.service.Files.Create(dst).Context(ctx).Media(reader, googleapi.ChunkSize(s.chunkSize)).Do()
  393. if err != nil {
  394. return err
  395. }
  396. return nil
  397. }
  398. // Retrieve a token, saves the token, then returns the generated client.
  399. func getGDriveClient(config *oauth2.Config, localConfigPath string, logger *log.Logger) *http.Client {
  400. tokenFile := filepath.Join(localConfigPath, GDriveTokenJsonFile)
  401. tok, err := gDriveTokenFromFile(tokenFile)
  402. if err != nil {
  403. tok = getGDriveTokenFromWeb(config, logger)
  404. saveGDriveToken(tokenFile, tok, logger)
  405. }
  406. return config.Client(context.Background(), tok)
  407. }
  408. // Request a token from the web, then returns the retrieved token.
  409. func getGDriveTokenFromWeb(config *oauth2.Config, logger *log.Logger) *oauth2.Token {
  410. authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
  411. fmt.Printf("Go to the following link in your browser then type the "+
  412. "authorization code: \n%v\n", authURL)
  413. var authCode string
  414. if _, err := fmt.Scan(&authCode); err != nil {
  415. logger.Fatalf("Unable to read authorization code %v", err)
  416. }
  417. tok, err := config.Exchange(context.TODO(), authCode)
  418. if err != nil {
  419. logger.Fatalf("Unable to retrieve token from web %v", err)
  420. }
  421. return tok
  422. }
  423. // Retrieves a token from a local file.
  424. func gDriveTokenFromFile(file string) (*oauth2.Token, error) {
  425. f, err := os.Open(file)
  426. defer f.Close()
  427. if err != nil {
  428. return nil, err
  429. }
  430. tok := &oauth2.Token{}
  431. err = json.NewDecoder(f).Decode(tok)
  432. return tok, err
  433. }
  434. // Saves a token to a file path.
  435. func saveGDriveToken(path string, token *oauth2.Token, logger *log.Logger) {
  436. logger.Printf("Saving credential file to: %s\n", path)
  437. f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
  438. defer f.Close()
  439. if err != nil {
  440. logger.Fatalf("Unable to cache oauth token: %v", err)
  441. }
  442. json.NewEncoder(f).Encode(token)
  443. }