github.com/qiniu/go-sdk/v7@v7.27.0/storage/uc.go (about)

     1  // package storage 提供了用户存储配置(uc)方面的功能, 定义了UC API 的返回结构体类型
     2  package storage
     3  
     4  import (
     5  	"context"
     6  	"net/url"
     7  	"strconv"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/qiniu/go-sdk/v7/storagev2/apis"
    12  	"github.com/qiniu/go-sdk/v7/storagev2/apis/get_bucket_event_rules"
    13  	"github.com/qiniu/go-sdk/v7/storagev2/apis/get_bucket_info"
    14  	"github.com/qiniu/go-sdk/v7/storagev2/apis/get_bucket_infos"
    15  	"github.com/qiniu/go-sdk/v7/storagev2/apis/set_bucket_cors_rules"
    16  	"github.com/qiniu/go-sdk/v7/storagev2/apis/set_bucket_taggings"
    17  )
    18  
    19  // BucketSummary 存储空间信息
    20  type BucketSummary struct {
    21  	// 存储空间名字
    22  	Name string     `json:"name"`
    23  	Info BucketInfo `json:"info"`
    24  }
    25  
    26  // BucketInfo 存储空间的详细信息
    27  type BucketInfo struct {
    28  	// 镜像回源地址, 接口返回的多个地址以;分割
    29  	Source string `json:"source"`
    30  
    31  	// 镜像回源的时候请求头中的HOST
    32  	Host string `json:"host"`
    33  
    34  	// 镜像回源地址过期时间(秒数), 现在这个功能没有实现,因此这个字段现在是没有意义的
    35  	Expires int `json:"expires"`
    36  
    37  	// 是否开启了原图保护
    38  	Protected int `json:"protected"`
    39  
    40  	// 是否是私有空间
    41  	Private int `json:"private"`
    42  
    43  	// 如果NoIndexPage是false表示开启了空间根目录index.html
    44  	// 如果是true, 表示没有开启
    45  	// 开启了根目录下的index.html, 文件将会被作为默认首页展示
    46  	NoIndexPage int `json:"no_index_page"`
    47  
    48  	// 在规定的时效内使客户端缓存更新的效果
    49  	MaxAge int `json:"max_age"`
    50  
    51  	// 图片样式分隔符, 接口返回的可能有多个
    52  	Separator string `json:"separator"`
    53  
    54  	// 图片样式, map中的key表示图片样式命令名字
    55  	// map中的value表示图片样式命令的内容
    56  	Styles map[string]string `json:"styles"`
    57  
    58  	// 防盗链模式
    59  	// 1 - 表示设置了防盗链的referer白名单
    60  	// 2 - 表示设置了防盗链的referer黑名单
    61  	AntiLeechMode int `json:"anti_leech_mode"`
    62  
    63  	// 使用token签名进行防盗链
    64  	// 0 - 表示关闭
    65  	// 1 - 表示开启
    66  	TokenAntiLeechMode int `json:"token_anti_leech"`
    67  
    68  	// 防盗链referer白名单列表
    69  	ReferWl []string `json:"refer_wl"`
    70  
    71  	// 防盗链referer黑名单列表
    72  	ReferBl []string `json:"refer_bl"`
    73  
    74  	// 在源站支持的情况下开启源站的Referer防盗链
    75  	EnableSource bool `json:"source_enabled"`
    76  
    77  	// 是否允许空的referer访问
    78  	NoRefer bool `json:"no_refer"`
    79  
    80  	// 用于防盗链token的生成
    81  	MacKey string `json:"mac_key"`
    82  
    83  	// 用于防盗链token的生成
    84  	MacKey2 string `json:"mac_key2"`
    85  
    86  	// 存储区域, 兼容保留
    87  	Zone string
    88  
    89  	// 存储区域
    90  	Region string
    91  
    92  	// 空间备注信息
    93  	Remark string
    94  
    95  	// 空间创建时间
    96  	// 当服务端未返回该字段时为零值,可通过 Ctime.IsZero() 判断
    97  	Ctime time.Time
    98  }
    99  
   100  // ReferAntiLeechConfig 是用户存储空间的Refer防盗链配置
   101  type ReferAntiLeechConfig struct {
   102  	// 防盗链模式, 0 - 关闭Refer防盗链, 1 - 开启Referer白名单,2 - 开启Referer黑名单
   103  	Mode int
   104  
   105  	// 是否允许空的referer访问
   106  	AllowEmptyReferer bool
   107  
   108  	// Pattern 匹配HTTP Referer头, 当模式是1或者2的时候有效
   109  	// Mode为1的时候表示允许Referer符合该Pattern的HTTP请求访问
   110  	// Mode为2的时候表示禁止Referer符合该Pattern的HTTP请求访问
   111  	// 当前允许的匹配字符串格式分为三种:
   112  	// 一种为空主机头域名, 比如 foo.com; 一种是泛域名, 比如 *.bar.com;
   113  	// 一种是完全通配符, 即一个 *;
   114  	// 多个规则之间用;隔开, 比如: foo.com;*.bar.com;sub.foo.com;*.sub.bar.com
   115  	Pattern string
   116  
   117  	// 是否开启源站的防盗链, 默认为0, 只开启CDN防盗链, 当设置为1的时候
   118  	// 在源站支持的情况下开启源站的Referer防盗链
   119  	EnableSource bool
   120  }
   121  
   122  // SetMode 设置referer防盗链模式
   123  func (r *ReferAntiLeechConfig) SetMode(mode int) *ReferAntiLeechConfig {
   124  	if mode != 0 && mode != 1 && mode != 2 {
   125  		panic("Referer anti_leech_mode must be in [0, 1, 2]")
   126  	}
   127  	r.Mode = mode
   128  	return r
   129  }
   130  
   131  // SetEmptyReferer 设置是否允许空Referer访问
   132  func (r *ReferAntiLeechConfig) SetEmptyReferer(enable bool) *ReferAntiLeechConfig {
   133  	r.AllowEmptyReferer = enable
   134  	return r
   135  }
   136  
   137  // SetPattern 设置匹配Referer的模式
   138  func (r *ReferAntiLeechConfig) SetPattern(pattern string) *ReferAntiLeechConfig {
   139  	if pattern == "" {
   140  		panic("Empty pattern is not allowed")
   141  	}
   142  
   143  	r.Pattern = pattern
   144  	return r
   145  }
   146  
   147  // AddDomainPattern 添加pattern到Pattern字段
   148  // 假入Pattern值为"*.qiniu.com", 使用AddDomainPattern("*.baidu.com")后
   149  // r.Pattern的值为"*.qiniu.com;*.baidu.com"
   150  func (r *ReferAntiLeechConfig) AddDomainPattern(pattern string) *ReferAntiLeechConfig {
   151  	if strings.HasSuffix(r.Pattern, ";") {
   152  		r.Pattern = strings.TrimRight(r.Pattern, ";")
   153  	}
   154  	r.Pattern = strings.Join([]string{r.Pattern, pattern}, ";")
   155  	return r
   156  }
   157  
   158  // SetEnableSource 设置是否开启源站的防盗链
   159  func (r *ReferAntiLeechConfig) SetEnableSource(enable bool) *ReferAntiLeechConfig {
   160  	r.EnableSource = enable
   161  	return r
   162  }
   163  
   164  // AsQueryString 编码成query参数格式
   165  func (r *ReferAntiLeechConfig) AsQueryString() string {
   166  	params := make(url.Values, 4)
   167  	params.Add("mode", strconv.Itoa(r.Mode))
   168  	params.Add("pattern", r.Pattern)
   169  	if r.AllowEmptyReferer {
   170  		params.Add("norefer", "1")
   171  	} else {
   172  		params.Add("norefer", "0")
   173  	}
   174  	if r.EnableSource {
   175  		params.Add("source_enabled", "1")
   176  	} else {
   177  		params.Add("source_enabled", "0")
   178  	}
   179  	return params.Encode()
   180  }
   181  
   182  // ProtectedOn 返回true or false
   183  // 如果开启了原图保护,返回true, 否则false
   184  func (b *BucketInfo) ProtectedOn() bool {
   185  	return b.Protected == 1
   186  }
   187  
   188  // IsPrivate  返回布尔值
   189  // 如果是私有空间, 返回 true, 否则返回false
   190  func (b *BucketInfo) IsPrivate() bool {
   191  	return b.Private == 1
   192  }
   193  
   194  // ImageSources 返回多个镜像回源地址的列表
   195  func (b *BucketInfo) ImageSources() (srcs []string) {
   196  	srcs = strings.Split(b.Source, ";")
   197  	return
   198  }
   199  
   200  // IndexPageOn 返回空间是否开启了根目录下的index.html
   201  func (b *BucketInfo) IndexPageOn() bool {
   202  	return b.NoIndexPage == 0
   203  }
   204  
   205  // Separators 返回分隔符列表
   206  func (b *BucketInfo) Separators() (ret []rune) {
   207  	for _, r := range b.Separator {
   208  		ret = append(ret, r)
   209  	}
   210  	return
   211  }
   212  
   213  // WhiteListSet 是否设置了防盗链白名单
   214  func (b *BucketInfo) WhiteListSet() bool {
   215  	return b.AntiLeechMode == 1
   216  }
   217  
   218  // BlackListSet 是否设置了防盗链黑名单
   219  func (b *BucketInfo) BlackListSet() bool {
   220  	return b.AntiLeechMode == 2
   221  }
   222  
   223  // TokenAntiLeechModeOn 返回是否使用token签名防盗链开启了
   224  func (b *BucketInfo) TokenAntiLeechModeOn() bool {
   225  	return b.TokenAntiLeechMode == 1
   226  }
   227  
   228  // parseBucketCreatedAt 解析服务端返回的 CreatedAt 字符串;空字符串视为零值(无错误)
   229  func parseBucketCreatedAt(createdAt string) (time.Time, error) {
   230  	if createdAt == "" {
   231  		return time.Time{}, nil
   232  	}
   233  	return time.Parse(time.RFC3339, createdAt)
   234  }
   235  
   236  // GetBucketInfo 返回BucketInfo结构
   237  func (m *BucketManager) GetBucketInfo(bucketName string) (BucketInfo, error) {
   238  	toBucketInfo := func(bucketInfo *get_bucket_info.Response) (BucketInfo, error) {
   239  		ctime, err := parseBucketCreatedAt(bucketInfo.CreatedAt)
   240  		if err != nil {
   241  			return BucketInfo{}, err
   242  		}
   243  		return BucketInfo{
   244  			Source:             bucketInfo.Source,
   245  			Host:               bucketInfo.Host,
   246  			Expires:            int(bucketInfo.Expires),
   247  			Protected:          int(bucketInfo.Protected),
   248  			Private:            int(bucketInfo.Private),
   249  			NoIndexPage:        int(bucketInfo.NoIndexPage),
   250  			MaxAge:             int(bucketInfo.MaxAge),
   251  			Separator:          bucketInfo.Separator,
   252  			Styles:             bucketInfo.Styles,
   253  			AntiLeechMode:      int(bucketInfo.AntiLeechMode),
   254  			TokenAntiLeechMode: int(bucketInfo.TokenAntiLeech),
   255  			ReferWl:            bucketInfo.ReferWl,
   256  			ReferBl:            bucketInfo.ReferBl,
   257  			EnableSource:       bucketInfo.SourceEnabled,
   258  			NoRefer:            bucketInfo.NoReferer,
   259  			MacKey:             bucketInfo.MacKey,
   260  			MacKey2:            bucketInfo.MacKey2,
   261  			Zone:               bucketInfo.Zone,
   262  			Region:             bucketInfo.Region,
   263  			Remark:             bucketInfo.Remark,
   264  			Ctime:              ctime,
   265  		}, nil
   266  	}
   267  	response, err := m.apiClient.GetBucketInfo(
   268  		context.Background(),
   269  		&apis.GetBucketInfoRequest{
   270  			Bucket: bucketName,
   271  		},
   272  		m.makeRequestOptions())
   273  	if err != nil {
   274  		return BucketInfo{}, err
   275  	}
   276  	return toBucketInfo(response)
   277  }
   278  
   279  // SetRemark 设置空间备注信息
   280  func (m *BucketManager) SetRemark(bucketName, remark string) error {
   281  	_, err := m.apiClient.SetBucketRemark(
   282  		context.Background(),
   283  		&apis.SetBucketRemarkRequest{
   284  			Bucket: bucketName,
   285  			Remark: remark,
   286  		},
   287  		m.makeRequestOptions(),
   288  	)
   289  	return err
   290  }
   291  
   292  // BucketInfosForRegion 获取指定区域的该用户的所有bucketInfo信息
   293  func (m *BucketManager) BucketInfosInRegion(region RegionID, statistics bool) (bucketInfos []BucketSummary, err error) {
   294  	toBucketInfo := func(bucketInfo *get_bucket_infos.BucketInfoV2) (BucketInfo, error) {
   295  		ctime, err := parseBucketCreatedAt(bucketInfo.CreatedAt)
   296  		if err != nil {
   297  			return BucketInfo{}, err
   298  		}
   299  		return BucketInfo{
   300  			Source:             bucketInfo.Source,
   301  			Host:               bucketInfo.Host,
   302  			Expires:            int(bucketInfo.Expires),
   303  			Protected:          int(bucketInfo.Protected),
   304  			Private:            int(bucketInfo.Private),
   305  			NoIndexPage:        int(bucketInfo.NoIndexPage),
   306  			MaxAge:             int(bucketInfo.MaxAge),
   307  			Separator:          bucketInfo.Separator,
   308  			Styles:             bucketInfo.Styles,
   309  			AntiLeechMode:      int(bucketInfo.AntiLeechMode),
   310  			TokenAntiLeechMode: int(bucketInfo.TokenAntiLeech),
   311  			ReferWl:            bucketInfo.ReferWl,
   312  			ReferBl:            bucketInfo.ReferBl,
   313  			EnableSource:       bucketInfo.SourceEnabled,
   314  			NoRefer:            bucketInfo.NoReferer,
   315  			MacKey:             bucketInfo.MacKey,
   316  			MacKey2:            bucketInfo.MacKey2,
   317  			Zone:               bucketInfo.Zone,
   318  			Region:             bucketInfo.Region,
   319  			Remark:             bucketInfo.Remark,
   320  			Ctime:              ctime,
   321  		}, nil
   322  	}
   323  	response, err := m.apiClient.GetBucketInfos(
   324  		context.Background(),
   325  		&apis.GetBucketInfosRequest{
   326  			Region:     string(region),
   327  			Statistics: statistics,
   328  		},
   329  		m.makeRequestOptions(),
   330  	)
   331  	if err != nil {
   332  		return nil, err
   333  	}
   334  	summaries := make([]BucketSummary, 0, len(response.AllBucketInfosV2))
   335  	for _, b := range response.AllBucketInfosV2 {
   336  		bucketInfo, err := toBucketInfo(&b.BucketInfo)
   337  		if err != nil {
   338  			return nil, err
   339  		}
   340  		summaries = append(summaries, BucketSummary{
   341  			Name: b.Name,
   342  			Info: bucketInfo,
   343  		})
   344  	}
   345  	return summaries, nil
   346  }
   347  
   348  // SetReferAntiLeechMode 配置存储空间referer防盗链模式
   349  func (m *BucketManager) SetReferAntiLeechMode(bucketName string, refererAntiLeechConfig *ReferAntiLeechConfig) error {
   350  	var allowEmptyReferer, enableSource int64
   351  	if refererAntiLeechConfig.AllowEmptyReferer {
   352  		allowEmptyReferer = 1
   353  	}
   354  	if refererAntiLeechConfig.EnableSource {
   355  		enableSource = 1
   356  	}
   357  	_, err := m.apiClient.SetBucketReferAntiLeech(
   358  		context.Background(),
   359  		&apis.SetBucketReferAntiLeechRequest{
   360  			Bucket:            bucketName,
   361  			Mode:              int64(refererAntiLeechConfig.Mode),
   362  			Pattern:           refererAntiLeechConfig.Pattern,
   363  			AllowEmptyReferer: allowEmptyReferer,
   364  			SourceEnabled:     enableSource,
   365  		},
   366  		m.makeRequestOptions(),
   367  	)
   368  	return err
   369  }
   370  
   371  // BucketLifeCycleRule 定义了关于七牛存储空间关于生命周期的一些配置,规则。
   372  // 比如存储空间中文件可以设置多少天后删除,多少天后转低频存储等等
   373  type BucketLifeCycleRule struct {
   374  	// 规则名称, 在设置的bucket中规则名称需要是唯一的
   375  	// 同时长度小于50, 不能为空
   376  	// 由字母,数字和下划线组成
   377  	Name string `json:"name"`
   378  
   379  	// 以该前缀开头的文件应用此规则
   380  	Prefix string `json:"prefix"`
   381  
   382  	// 指定存储空间内的文件多少天后删除
   383  	// 0 - 不删除
   384  	// > 0 表示多少天后删除
   385  	DeleteAfterDays int `json:"delete_after_days"`
   386  
   387  	// 在多少天后转低频存储
   388  	// 0  - 表示不转低频
   389  	// > 0 表示多少天后转低频存储
   390  	ToLineAfterDays int `json:"to_line_after_days"`
   391  
   392  	// 指定文件上传多少天后转归档直读存储。
   393  	// 0 表示不转归档直读存储,
   394  	// > 0 表示多少天后转归档直读存储
   395  	ToArchiveIRAfterDays int `json:"to_archive_ir_after_days"`
   396  
   397  	// 指定文件上传多少天后转归档存储。
   398  	// 0 表示不转归档存储,
   399  	// > 0 表示多少天后转归档存储
   400  	ToArchiveAfterDays int `json:"to_archive_after_days"`
   401  
   402  	// 指定文件上传多少天后转深度归档存储。
   403  	// 0 表示不转深度归档存储
   404  	// > 0 表示多少天后转深度归档存储
   405  	ToDeepArchiveAfterDays int `json:"to_deep_archive_after_days"`
   406  }
   407  
   408  // SetBucketLifeCycleRule 设置存储空间内文件的生命周期规则
   409  func (m *BucketManager) AddBucketLifeCycleRule(bucketName string, lifeCycleRule *BucketLifeCycleRule) error {
   410  	_, err := m.apiClient.AddBucketRules(
   411  		context.Background(),
   412  		&apis.AddBucketRulesRequest{
   413  			Bucket:                 bucketName,
   414  			Name:                   lifeCycleRule.Name,
   415  			Prefix:                 lifeCycleRule.Prefix,
   416  			DeleteAfterDays:        int64(lifeCycleRule.DeleteAfterDays),
   417  			ToIaAfterDays:          int64(lifeCycleRule.ToLineAfterDays),
   418  			ToArchiveAfterDays:     int64(lifeCycleRule.ToArchiveAfterDays),
   419  			ToArchiveIrAfterDays:   int64(lifeCycleRule.ToArchiveIRAfterDays),
   420  			ToDeepArchiveAfterDays: int64(lifeCycleRule.ToDeepArchiveAfterDays),
   421  		},
   422  		m.makeRequestOptions(),
   423  	)
   424  	return err
   425  }
   426  
   427  // DelBucketLifeCycleRule 删除特定存储空间上设定的规则
   428  func (m *BucketManager) DelBucketLifeCycleRule(bucketName, ruleName string) error {
   429  	_, err := m.apiClient.DeleteBucketRules(
   430  		context.Background(),
   431  		&apis.DeleteBucketRulesRequest{
   432  			Bucket: bucketName,
   433  			Name:   ruleName,
   434  		},
   435  		m.makeRequestOptions(),
   436  	)
   437  	return err
   438  }
   439  
   440  // UpdateBucketLifeCycleRule 更新特定存储空间上的生命周期规则
   441  func (m *BucketManager) UpdateBucketLifeCycleRule(bucketName string, rule *BucketLifeCycleRule) error {
   442  	_, err := m.apiClient.UpdateBucketRules(
   443  		context.Background(),
   444  		&apis.UpdateBucketRulesRequest{
   445  			Bucket:                 bucketName,
   446  			Name:                   rule.Name,
   447  			Prefix:                 rule.Prefix,
   448  			DeleteAfterDays:        int64(rule.DeleteAfterDays),
   449  			ToIaAfterDays:          int64(rule.ToLineAfterDays),
   450  			ToArchiveAfterDays:     int64(rule.ToArchiveAfterDays),
   451  			ToArchiveIrAfterDays:   int64(rule.ToArchiveIRAfterDays),
   452  			ToDeepArchiveAfterDays: int64(rule.ToDeepArchiveAfterDays),
   453  		},
   454  		m.makeRequestOptions(),
   455  	)
   456  	return err
   457  }
   458  
   459  // GetBucketLifeCycleRule 获取指定空间上设置的生命周期规则
   460  func (m *BucketManager) GetBucketLifeCycleRule(bucketName string) ([]BucketLifeCycleRule, error) {
   461  	response, err := m.apiClient.GetBucketRules(
   462  		context.Background(),
   463  		&apis.GetBucketRulesRequest{
   464  			Bucket: bucketName,
   465  		},
   466  		m.makeRequestOptions(),
   467  	)
   468  	if err != nil {
   469  		return nil, err
   470  	}
   471  	rules := make([]BucketLifeCycleRule, 0, len(response.BucketRules))
   472  	for _, rule := range response.BucketRules {
   473  		rules = append(rules, BucketLifeCycleRule{
   474  			Name:                   rule.Name,
   475  			Prefix:                 rule.Prefix,
   476  			DeleteAfterDays:        int(rule.DeleteAfterDays),
   477  			ToLineAfterDays:        int(rule.ToIaAfterDays),
   478  			ToArchiveAfterDays:     int(rule.ToArchiveAfterDays),
   479  			ToDeepArchiveAfterDays: int(rule.ToDeepArchiveAfterDays),
   480  		})
   481  	}
   482  	return rules, nil
   483  }
   484  
   485  // BucketEnvent 定义了存储空间发生事件时候的通知规则
   486  // 比如调用了存储的"delete"删除接口删除文件, 这个是一个事件;
   487  // 当这个事件发生的时候, 我们要对哪些文件,做什么处理,是否要作回调,
   488  // 都可以通过这个结构体配置
   489  type BucketEventRule struct {
   490  	// 规则名字
   491  	Name string `json:"name"`
   492  
   493  	// 匹配文件前缀
   494  	Prefix string `json:"prefix"`
   495  
   496  	// 匹配文件后缀
   497  	Suffix string `json:"suffix"`
   498  
   499  	// 事件类型
   500  	// put,mkfile,delete,copy,move,append,disable,enable,deleteMarkerCreate
   501  	Event []string `json:"event"`
   502  
   503  	// 回调通知地址, 可以指定多个
   504  	CallbackURL []string `json:"callback_urls"`
   505  
   506  	// 用户的AccessKey, 可选, 设置的话会对通知请求用对应的ak、sk进行签名
   507  	AccessKey string `json:"access_key"`
   508  
   509  	// 回调通知的请求HOST, 可选
   510  	Host string `json:"host"`
   511  }
   512  
   513  // Params 返回一个hash结构
   514  func (r *BucketEventRule) Params(bucket string) map[string][]string {
   515  	params := make(map[string][]string)
   516  
   517  	params["bucket"] = []string{bucket}
   518  	params["name"] = []string{r.Name}
   519  	if r.Prefix != "" {
   520  		params["prefix"] = []string{r.Prefix}
   521  	}
   522  	if r.Suffix != "" {
   523  		params["suffix"] = []string{r.Suffix}
   524  	}
   525  	params["event"] = r.Event
   526  	params["callbackURL"] = r.CallbackURL
   527  	if r.AccessKey != "" {
   528  		params["access_key"] = []string{r.AccessKey}
   529  	}
   530  	if r.Host != "" {
   531  		params["host"] = []string{r.Host}
   532  	}
   533  	return params
   534  }
   535  
   536  // AddBucketEvent 增加存储空间事件通知规则
   537  func (m *BucketManager) AddBucketEvent(bucket string, rule *BucketEventRule) error {
   538  	_, err := m.apiClient.AddBucketEventRule(
   539  		context.Background(),
   540  		&apis.AddBucketEventRuleRequest{
   541  			Bucket:       bucket,
   542  			Name:         rule.Name,
   543  			Prefix:       rule.Prefix,
   544  			Suffix:       rule.Suffix,
   545  			EventTypes:   rule.Event,
   546  			CallbackUrls: rule.CallbackURL,
   547  			AccessKey:    rule.AccessKey,
   548  			Host:         rule.Host,
   549  		},
   550  		m.makeRequestOptions(),
   551  	)
   552  	return err
   553  }
   554  
   555  // DelBucketEvent 删除指定存储空间的通知事件规则
   556  func (m *BucketManager) DelBucketEvent(bucket, ruleName string) error {
   557  	_, err := m.apiClient.DeleteBucketEventRule(
   558  		context.Background(),
   559  		&apis.DeleteBucketEventRuleRequest{
   560  			Bucket: bucket,
   561  			Name:   ruleName,
   562  		},
   563  		m.makeRequestOptions(),
   564  	)
   565  	return err
   566  }
   567  
   568  // UpdateBucketEnvent 更新指定存储空间的事件通知规则
   569  func (m *BucketManager) UpdateBucketEnvent(bucket string, rule *BucketEventRule) error {
   570  	_, err := m.apiClient.UpdateBucketEventRule(
   571  		context.Background(),
   572  		&apis.UpdateBucketEventRuleRequest{
   573  			Bucket:       bucket,
   574  			Name:         rule.Name,
   575  			Prefix:       rule.Prefix,
   576  			Suffix:       rule.Suffix,
   577  			EventTypes:   rule.Event,
   578  			CallbackUrls: rule.CallbackURL,
   579  			AccessKey:    rule.AccessKey,
   580  			Host:         rule.Host,
   581  		},
   582  		m.makeRequestOptions(),
   583  	)
   584  	return err
   585  }
   586  
   587  // GetBucketEvent 获取指定存储空间的事件通知规则
   588  func (m *BucketManager) GetBucketEvent(bucket string) ([]BucketEventRule, error) {
   589  	toBucketEventRule := func(rule *get_bucket_event_rules.BucketEventRule) BucketEventRule {
   590  		return BucketEventRule{
   591  			Name:        rule.Name,
   592  			Prefix:      rule.Prefix,
   593  			Suffix:      rule.Suffix,
   594  			Event:       rule.EventTypes,
   595  			CallbackURL: rule.CallbackUrls,
   596  			AccessKey:   rule.AccessKey,
   597  			Host:        rule.Host,
   598  		}
   599  	}
   600  	response, err := m.apiClient.GetBucketEventRules(
   601  		context.Background(),
   602  		&apis.GetBucketEventRulesRequest{
   603  			Bucket: bucket,
   604  		},
   605  		m.makeRequestOptions(),
   606  	)
   607  	if err != nil {
   608  		return nil, err
   609  	}
   610  	rules := make([]BucketEventRule, 0, len(response.BucketEventRules))
   611  	for _, r := range response.BucketEventRules {
   612  		rules = append(rules, toBucketEventRule(&r))
   613  	}
   614  	return rules, err
   615  }
   616  
   617  // CorsRule 是关于存储的跨域规则
   618  // 最多允许设置10条跨域规则
   619  // 对于同一个域名如果设置了多条规则,那么按顺序使用第一条匹配的规则去生成返回值。
   620  // 对于简单跨域请求,只匹配 Origin;
   621  // 对于预检请求, 需要匹配 Origin、AllowedMethod、AllowedHeader;
   622  
   623  // 如果没有设置任何corsRules,那么默认允许所有的跨域请求
   624  // 参考: https://www.w3.org/TR/cors/
   625  type CorsRule struct {
   626  	// allowed_orgin: 允许的域名。必填;支持通配符*;*表示全部匹配;只有第一个*生效;需要设置"Scheme";大小写敏感。例如
   627  	//   规则:http://*.abc.*.com 请求:"http://test.abc.test.com" 结果:不通过
   628  	//   规则:"http://abc.com" 请求:"https://abc.com"/"abc.com" 结果:不通过
   629  	//   规则:"abc.com" 请求:"http://abc.com" 结果:不通过
   630  	AllowedOrigin []string `json:"allowed_origin"`
   631  
   632  	// allowed_method: 允许的方法。必填;不支持通配符;大小写不敏感;
   633  	AllowedMethod []string `json:"allowed_method"`
   634  
   635  	// allowed_header: 允许的header。选填;支持通配符*,但只能是单独的*,表示允许全部header,其他*不生效;空则不允许任何header;大小写不敏感;
   636  	AllowedHeader []string `json:"allowed_header"`
   637  
   638  	// 暴露的header。选填;不支持通配符;X-Log, X-Reqid是默认会暴露的两个header;其他的header如果没有设置,则不会暴露;大小写不敏感;
   639  	ExposedHeader []string `json:"exposed_header"`
   640  
   641  	// max_age: 结果可以缓存的时间。选填;空则不缓存
   642  	MaxAge int64 `json:"max_age"`
   643  }
   644  
   645  // AddCorsRules 设置指定存储空间的跨域规则
   646  func (m *BucketManager) AddCorsRules(bucket string, corsRules []CorsRule) error {
   647  	rules := make(set_bucket_cors_rules.CORSRules, 0, len(corsRules))
   648  	for _, rule := range corsRules {
   649  		rules = append(rules, set_bucket_cors_rules.CORSRule{
   650  			AllowedOrigin: rule.AllowedMethod,
   651  			AllowedMethod: rule.AllowedMethod,
   652  			AllowedHeader: rule.AllowedHeader,
   653  			ExposedHeader: rule.ExposedHeader,
   654  			MaxAge:        rule.MaxAge,
   655  		})
   656  	}
   657  	_, err := m.apiClient.SetBucketCORSRules(
   658  		context.Background(),
   659  		&apis.SetBucketCORSRulesRequest{
   660  			Bucket:    bucket,
   661  			CORSRules: rules,
   662  		},
   663  		m.makeRequestOptions(),
   664  	)
   665  	return err
   666  }
   667  
   668  // GetCorsRules 获取指定存储空间的跨域规则
   669  func (m *BucketManager) GetCorsRules(bucket string) ([]CorsRule, error) {
   670  	response, err := m.apiClient.GetBucketCORSRules(
   671  		context.Background(),
   672  		&apis.GetBucketCORSRulesRequest{
   673  			Bucket: bucket,
   674  		},
   675  		m.makeRequestOptions(),
   676  	)
   677  	if err != nil {
   678  		return nil, err
   679  	}
   680  	rules := make([]CorsRule, 0, len(response.CORSRules))
   681  	for _, rule := range response.CORSRules {
   682  		rules = append(rules, CorsRule{
   683  			AllowedOrigin: rule.AllowedOrigin,
   684  			AllowedMethod: rule.AllowedMethod,
   685  			AllowedHeader: rule.AllowedHeader,
   686  			ExposedHeader: rule.ExposedHeader,
   687  			MaxAge:        rule.MaxAge,
   688  		})
   689  	}
   690  	return rules, nil
   691  }
   692  
   693  // BucketQuota 七牛存储空间的配额信息
   694  type BucketQuota struct {
   695  	// 如果HTTP请求没有发送该参数或者发送的参数是0,表示不更改当前配置
   696  	// 如果是-1, 表示取消限额
   697  	// 一下两个参数都使用于这个逻辑
   698  
   699  	// 空间存储量配额信息
   700  	Size int64
   701  
   702  	// 空间文件数配置信息
   703  	Count int64
   704  }
   705  
   706  // SetBucketQuota 设置存储空间的配额限制
   707  // 配额限制主要是两块, 空间存储量的限制和空间文件数限制
   708  func (m *BucketManager) SetBucketQuota(bucket string, size, count int64) error {
   709  	_, err := m.apiClient.SetBucketQuota(
   710  		context.Background(),
   711  		&apis.SetBucketQuotaRequest{
   712  			Bucket: bucket,
   713  			Size:   size,
   714  			Count:  count,
   715  		},
   716  		m.makeRequestOptions(),
   717  	)
   718  	return err
   719  }
   720  
   721  // GetBucketQuota 获取存储空间的配额信息
   722  func (m *BucketManager) GetBucketQuota(bucket string) (quota BucketQuota, err error) {
   723  	response, err := m.apiClient.GetBucketQuota(
   724  		context.Background(),
   725  		&apis.GetBucketQuotaRequest{
   726  			Bucket: bucket,
   727  		},
   728  		m.makeRequestOptions(),
   729  	)
   730  	if err != nil {
   731  		return BucketQuota{}, err
   732  	}
   733  	return BucketQuota{
   734  		Size:  response.Size,
   735  		Count: response.Count,
   736  	}, nil
   737  }
   738  
   739  // SetBucketAccessStyle 可以用来开启或关闭制定存储空间的原图保护
   740  // mode - 1 ==> 开启原图保护
   741  // mode - 0 ==> 关闭原图保护
   742  func (m *BucketManager) SetBucketAccessStyle(bucket string, mode int) error {
   743  	_, err := m.apiClient.SetBucketAccessMode(
   744  		context.Background(),
   745  		&apis.SetBucketAccessModeRequest{
   746  			Bucket: bucket,
   747  			Mode:   int64(mode),
   748  		},
   749  		m.makeRequestOptions(),
   750  	)
   751  	return err
   752  }
   753  
   754  // TurnOffBucketProtected 开启指定存储空间的原图保护
   755  func (m *BucketManager) TurnOnBucketProtected(bucket string) error {
   756  	return m.SetBucketAccessStyle(bucket, 1)
   757  }
   758  
   759  // TurnOffBucketProtected 关闭指定空间的原图保护
   760  func (m *BucketManager) TurnOffBucketProtected(bucket string) error {
   761  	return m.SetBucketAccessStyle(bucket, 0)
   762  }
   763  
   764  // SetBucketMaxAge 设置指定存储空间的MaxAge响应头
   765  // maxAge <= 0时,表示使用默认值31536000
   766  func (m *BucketManager) SetBucketMaxAge(bucket string, maxAge int64) error {
   767  	_, err := m.apiClient.SetBucketMaxAge(
   768  		context.Background(),
   769  		&apis.SetBucketMaxAgeRequest{
   770  			Bucket: bucket,
   771  			MaxAge: maxAge,
   772  		},
   773  		m.makeRequestOptions(),
   774  	)
   775  	return err
   776  }
   777  
   778  // SetBucketAccessMode 设置指定空间的私有属性
   779  
   780  // mode - 1 表示设置空间为私有空间, 私有空间访问需要鉴权
   781  // mode - 0 表示设置空间为公开空间
   782  func (m *BucketManager) SetBucketAccessMode(bucket string, mode int) error {
   783  	_, err := m.apiClient.SetBucketPrivate(
   784  		context.Background(),
   785  		&apis.SetBucketPrivateRequest{
   786  			Bucket:    bucket,
   787  			IsPrivate: int64(mode),
   788  		},
   789  		m.makeRequestOptions(),
   790  	)
   791  	return err
   792  }
   793  
   794  // MakeBucketPublic 设置空间为公有空间
   795  func (m *BucketManager) MakeBucketPublic(bucket string) error {
   796  	return m.SetBucketAccessMode(bucket, 0)
   797  }
   798  
   799  // MakeBucketPrivate 设置空间为私有空间
   800  func (m *BucketManager) MakeBucketPrivate(bucket string) error {
   801  	return m.SetBucketAccessMode(bucket, 1)
   802  }
   803  
   804  // TurnOnIndexPage 设置默认首页
   805  func (m *BucketManager) TurnOnIndexPage(bucket string) error {
   806  	return m.setIndexPage(bucket, 0)
   807  }
   808  
   809  // TurnOnIndexPage 关闭默认首页
   810  func (m *BucketManager) TurnOffIndexPage(bucket string) error {
   811  	return m.setIndexPage(bucket, 1)
   812  }
   813  
   814  func (m *BucketManager) setIndexPage(bucket string, noIndexPage int) error {
   815  	_, err := m.apiClient.DisableBucketIndexPage(
   816  		context.Background(),
   817  		&apis.DisableBucketIndexPageRequest{
   818  			Bucket:      bucket,
   819  			NoIndexPage: int64(noIndexPage),
   820  		},
   821  		m.makeRequestOptions(),
   822  	)
   823  	return err
   824  }
   825  
   826  // BucketTagging 为 Bucket 设置标签
   827  type BucketTagging struct {
   828  	Tags []BucketTag `json:"Tags"`
   829  }
   830  
   831  type BucketTag struct {
   832  	Key   string `json:"Key"`
   833  	Value string `json:"Value"`
   834  }
   835  
   836  // SetTagging 设置 Bucket 标签
   837  
   838  // SetTagging 该方法为覆盖所有 Bucket 上之前设置的标签,标签 Key 最大 64 字节,Value 最大 128 字节,均不能为空,且区分大小写
   839  // Key 不能以 kodo 为前缀,Key 和 Value 的字符只能为:字母,数字,空格,+,-,=,.,_,:,/,@,不能支持中文
   840  func (m *BucketManager) SetTagging(bucket string, tags map[string]string) error {
   841  	tagPairs := make(set_bucket_taggings.Tags, 0, len(tags))
   842  	for k, v := range tags {
   843  		tagPairs = append(tagPairs, set_bucket_taggings.TagInfo{Key: k, Value: v})
   844  	}
   845  	_, err := m.apiClient.SetBucketTaggings(
   846  		context.Background(),
   847  		&apis.SetBucketTaggingsRequest{
   848  			Bucket: bucket,
   849  			Tags:   tagPairs,
   850  		},
   851  		m.makeRequestOptions(),
   852  	)
   853  	return err
   854  }
   855  
   856  // ClearTagging 清空 Bucket 标签
   857  func (m *BucketManager) ClearTagging(bucket string) error {
   858  	_, err := m.apiClient.DeleteBucketTaggings(
   859  		context.Background(),
   860  		&apis.DeleteBucketTaggingsRequest{
   861  			BucketName: bucket,
   862  		},
   863  		m.makeRequestOptions(),
   864  	)
   865  	return err
   866  }
   867  
   868  // GetTagging 获取 Bucket 标签
   869  func (m *BucketManager) GetTagging(bucket string) (map[string]string, error) {
   870  	response, err := m.apiClient.GetBucketTaggings(
   871  		context.Background(),
   872  		&apis.GetBucketTaggingsRequest{
   873  			BucketName: bucket,
   874  		},
   875  		m.makeRequestOptions(),
   876  	)
   877  	if err != nil {
   878  		return nil, err
   879  	}
   880  	tags := make(map[string]string, len(response.Tags))
   881  	for _, tag := range response.Tags {
   882  		tags[tag.Key] = tag.Value
   883  	}
   884  	return tags, nil
   885  }