qinium 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +13 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +68 -0
- data/LICENSE.txt +21 -0
- data/README.md +33 -0
- data/Rakefile +12 -0
- data/lib/qinium/auth.rb +36 -0
- data/lib/qinium/client.rb +138 -0
- data/lib/qinium/config.rb +66 -0
- data/lib/qinium/configurable.rb +26 -0
- data/lib/qinium/errors.rb +14 -0
- data/lib/qinium/host_manager.rb +91 -0
- data/lib/qinium/object.rb +50 -0
- data/lib/qinium/put_policy.rb +91 -0
- data/lib/qinium/utils.rb +43 -0
- data/lib/qinium/version.rb +3 -0
- data/lib/qinium.rb +39 -0
- data/qinium.gemspec +30 -0
- data/sig/qinium.rbs +4 -0
- metadata +68 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 97006dadb420603b659536192b0d996ddb925d6ec024fc5553970b437b45553e
|
4
|
+
data.tar.gz: 2de51b37c7a5e8c314724c2594525885e086ea5d764888b922d6f91cec2e1c66
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5f907d3494a958568d0aeabfc978133b1eabd46f04da4e55e536da7b8611e0bec2dc0f1a92772c4e588ad5c9f5f6f46c6abb6d5e9842abc79f091de5f4b5553e
|
7
|
+
data.tar.gz: ddc54d441f0dca3e48a6f7a14762f7b5d37624e10100a2561b1ef06bd8541c3156c5ed6d72fec2b194b5d307cb55803be874f67028349c848254a4c3127eb174
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
# Specify your gem's dependencies in qinium.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
gem "rake", "~> 13.0"
|
9
|
+
|
10
|
+
gem "rspec", "~> 3.0"
|
11
|
+
|
12
|
+
gem "rubocop", "~> 1.21"
|
13
|
+
|
14
|
+
gem "vcr", "~> 6.1"
|
15
|
+
|
16
|
+
gem "webmock", "~> 3.17"
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
qinium (0.1.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
addressable (2.8.0)
|
10
|
+
public_suffix (>= 2.0.2, < 5.0)
|
11
|
+
ast (2.4.2)
|
12
|
+
crack (0.4.5)
|
13
|
+
rexml
|
14
|
+
diff-lcs (1.5.0)
|
15
|
+
hashdiff (1.0.1)
|
16
|
+
parallel (1.22.1)
|
17
|
+
parser (3.1.1.0)
|
18
|
+
ast (~> 2.4.1)
|
19
|
+
public_suffix (4.0.7)
|
20
|
+
rainbow (3.1.1)
|
21
|
+
rake (13.0.6)
|
22
|
+
regexp_parser (2.5.0)
|
23
|
+
rexml (3.2.5)
|
24
|
+
rspec (3.11.0)
|
25
|
+
rspec-core (~> 3.11.0)
|
26
|
+
rspec-expectations (~> 3.11.0)
|
27
|
+
rspec-mocks (~> 3.11.0)
|
28
|
+
rspec-core (3.11.0)
|
29
|
+
rspec-support (~> 3.11.0)
|
30
|
+
rspec-expectations (3.11.0)
|
31
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
32
|
+
rspec-support (~> 3.11.0)
|
33
|
+
rspec-mocks (3.11.1)
|
34
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
35
|
+
rspec-support (~> 3.11.0)
|
36
|
+
rspec-support (3.11.0)
|
37
|
+
rubocop (1.27.0)
|
38
|
+
parallel (~> 1.10)
|
39
|
+
parser (>= 3.1.0.0)
|
40
|
+
rainbow (>= 2.2.2, < 4.0)
|
41
|
+
regexp_parser (>= 1.8, < 3.0)
|
42
|
+
rexml
|
43
|
+
rubocop-ast (>= 1.16.0, < 2.0)
|
44
|
+
ruby-progressbar (~> 1.7)
|
45
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
46
|
+
rubocop-ast (1.17.0)
|
47
|
+
parser (>= 3.1.1.0)
|
48
|
+
ruby-progressbar (1.11.0)
|
49
|
+
unicode-display_width (2.1.0)
|
50
|
+
vcr (6.1.0)
|
51
|
+
webmock (3.17.1)
|
52
|
+
addressable (>= 2.8.0)
|
53
|
+
crack (>= 0.3.2)
|
54
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
55
|
+
|
56
|
+
PLATFORMS
|
57
|
+
ruby
|
58
|
+
|
59
|
+
DEPENDENCIES
|
60
|
+
qinium!
|
61
|
+
rake (~> 13.0)
|
62
|
+
rspec (~> 3.0)
|
63
|
+
rubocop (~> 1.21)
|
64
|
+
vcr (~> 6.1)
|
65
|
+
webmock (~> 3.17)
|
66
|
+
|
67
|
+
BUNDLED WITH
|
68
|
+
2.3.9
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2022 张小辉
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# Qinium
|
2
|
+
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/qinium`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
+
|
5
|
+
TODO: Delete this and the text above, and describe your gem
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Install the gem and add to the application's Gemfile by executing:
|
10
|
+
|
11
|
+
$ bundle add qinium
|
12
|
+
|
13
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
14
|
+
|
15
|
+
$ gem install qinium
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
TODO: Write usage instructions here
|
20
|
+
|
21
|
+
## Development
|
22
|
+
|
23
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
24
|
+
|
25
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
26
|
+
|
27
|
+
## Contributing
|
28
|
+
|
29
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/qinium.
|
30
|
+
|
31
|
+
## License
|
32
|
+
|
33
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/lib/qinium/auth.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
class Qinium
|
2
|
+
module Auth
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def calculate_hmac_sha1_digest(secret_key, str)
|
6
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest.new("sha1"), secret_key, str)
|
7
|
+
end
|
8
|
+
|
9
|
+
def generate_access_token(access_key, secret_key, url, body = {})
|
10
|
+
encoded_sign = generate_access_token_sign_with_mac(secret_key, url, body)
|
11
|
+
|
12
|
+
"#{access_key}:#{encoded_sign}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def generate_access_token_sign_with_mac(secret_key, url, body)
|
16
|
+
### 解析URL,生成待签名字符串
|
17
|
+
uri = URI.parse(url)
|
18
|
+
signing_str = uri.path
|
19
|
+
|
20
|
+
# 如有QueryString部分,则需要加上
|
21
|
+
query_string = uri.query
|
22
|
+
signing_str += "?" + query_string if query_string.is_a?(String) && !query_string.empty?
|
23
|
+
|
24
|
+
# 追加换行符
|
25
|
+
signing_str += "\n"
|
26
|
+
|
27
|
+
# 如果有Body,则也加上
|
28
|
+
# (仅限于mime == "application/x-www-form-urlencoded"的情况)
|
29
|
+
signing_str += body if body.is_a?(String) && !body.strip.empty?
|
30
|
+
|
31
|
+
### 生成数字签名
|
32
|
+
sign = calculate_hmac_sha1_digest(secret_key, signing_str)
|
33
|
+
Utils.urlsafe_base64_encode(sign)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
class Qinium
|
2
|
+
class Client
|
3
|
+
include Qinium::Utils
|
4
|
+
|
5
|
+
attr_reader :config
|
6
|
+
|
7
|
+
def initialize(config)
|
8
|
+
@config = config
|
9
|
+
end
|
10
|
+
|
11
|
+
# rubocop: disable Style/SingleLineMethods
|
12
|
+
def user_agent; config.user_agent end
|
13
|
+
def access_key; config.access_key end
|
14
|
+
def secret_key; config.secret_key end
|
15
|
+
# rubocop: enable Style/SingleLineMethods
|
16
|
+
|
17
|
+
def get(url, opts = {})
|
18
|
+
opts[:raise] = true unless opts.key?(:raise)
|
19
|
+
req_headers = {
|
20
|
+
connection: "close",
|
21
|
+
accept: "*/*",
|
22
|
+
user_agent: user_agent
|
23
|
+
}
|
24
|
+
req_headers.merge!(opts[:headers]) if opts[:headers].is_a?(Hash)
|
25
|
+
|
26
|
+
uri = URI(url)
|
27
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
28
|
+
if uri.scheme == "https"
|
29
|
+
http.use_ssl = true
|
30
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
31
|
+
end
|
32
|
+
|
33
|
+
logger.debug "Get #{uri}"
|
34
|
+
logger.debug " options: #{opts.to_json}"
|
35
|
+
|
36
|
+
req = Net::HTTP::Get.new(uri)
|
37
|
+
req_headers.each_pair do |key, value|
|
38
|
+
req.add_field key, value
|
39
|
+
end
|
40
|
+
res = http.request(req)
|
41
|
+
code = res.code.to_i
|
42
|
+
body = res.body
|
43
|
+
headers = res.header.to_hash.transform_values do |v|
|
44
|
+
v.join(", ")
|
45
|
+
end
|
46
|
+
|
47
|
+
if headers["content-type"] == "application/json"
|
48
|
+
body = begin
|
49
|
+
JSON.parse(body)
|
50
|
+
rescue StandardError
|
51
|
+
{}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
raise APIError.new(code, body, headers) if opts[:raise] && !success?(code)
|
55
|
+
|
56
|
+
[code, body, headers]
|
57
|
+
end
|
58
|
+
|
59
|
+
def post(url, req_body = "", opts = {})
|
60
|
+
opts[:raise] = true unless opts.key?(:raise)
|
61
|
+
req_headers = {
|
62
|
+
connection: "close",
|
63
|
+
accept: "*/*",
|
64
|
+
user_agent: user_agent
|
65
|
+
}
|
66
|
+
req_headers.merge!(opts[:headers]) if opts[:headers].is_a?(Hash)
|
67
|
+
|
68
|
+
uri = URI(url)
|
69
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
70
|
+
if uri.scheme == "https"
|
71
|
+
http.use_ssl = true
|
72
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
73
|
+
end
|
74
|
+
|
75
|
+
logger.debug "POST #{uri}"
|
76
|
+
logger.debug " body: #{req_body[0, 50]}"
|
77
|
+
logger.debug " headers: #{opts.to_json}"
|
78
|
+
|
79
|
+
req = Net::HTTP::Post.new(uri)
|
80
|
+
req_headers.each_pair do |key, value|
|
81
|
+
req.add_field key, value
|
82
|
+
end
|
83
|
+
req.body = req_body
|
84
|
+
res = http.request(req)
|
85
|
+
code = res.code.to_i
|
86
|
+
body = res.body
|
87
|
+
headers = res.header.to_hash.transform_values do |v|
|
88
|
+
v.join(", ")
|
89
|
+
end
|
90
|
+
if headers["content-type"] == "application/json"
|
91
|
+
body = begin
|
92
|
+
JSON.parse(body)
|
93
|
+
rescue StandardError
|
94
|
+
nil
|
95
|
+
end
|
96
|
+
end
|
97
|
+
raise APIError.new(code, body, headers) if opts[:raise] && !success?(code)
|
98
|
+
|
99
|
+
[code, body, headers]
|
100
|
+
end
|
101
|
+
|
102
|
+
def management_get(url)
|
103
|
+
access_token = Auth.generate_access_token(access_key, secret_key, url)
|
104
|
+
get(url, headers: {
|
105
|
+
"Content-Type" => "application/x-www-form-urlencoded",
|
106
|
+
"Authorization" => "QBox " + access_token
|
107
|
+
})
|
108
|
+
end
|
109
|
+
|
110
|
+
def management_post(url, body = nil)
|
111
|
+
body = JSON.dump(body) if body && body.size > 0
|
112
|
+
access_token = Auth.generate_access_token(access_key, secret_key, url, body)
|
113
|
+
post(url, body, headers: {
|
114
|
+
"Content-Type" => "application/x-www-form-urlencoded",
|
115
|
+
"Authorization" => "QBox " + access_token
|
116
|
+
})
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.success?(http_code)
|
120
|
+
http_code.between?(200, 299)
|
121
|
+
end
|
122
|
+
|
123
|
+
def success?(http_code)
|
124
|
+
self.class.success?(http_code)
|
125
|
+
end
|
126
|
+
|
127
|
+
def self.to_query(params)
|
128
|
+
if params.is_a?(Hash)
|
129
|
+
total_param = params.map do |key, value|
|
130
|
+
%(#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s).gsub("+", "%20")})
|
131
|
+
end
|
132
|
+
return total_param.join("&")
|
133
|
+
end
|
134
|
+
|
135
|
+
params
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
class Qinium
|
2
|
+
class Config < Configurable
|
3
|
+
def initialize(options = {})
|
4
|
+
options[:protocol] ||= :https
|
5
|
+
options = if options[:protocol] == :http
|
6
|
+
http_options.merge(options)
|
7
|
+
else
|
8
|
+
https_options.merge(options)
|
9
|
+
end
|
10
|
+
super(default_options.merge(options))
|
11
|
+
end
|
12
|
+
|
13
|
+
def up_host(bucket = self.bucket)
|
14
|
+
HostManager.new(self).up_host(bucket)
|
15
|
+
end
|
16
|
+
|
17
|
+
def put_policy_options
|
18
|
+
@put_policy_options ||= self[:put_policy_options] || Configurable.new
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def default_options
|
24
|
+
{
|
25
|
+
user_agent: "QiniumRuby/#{VERSION} (#{RUBY_PLATFORM}) Ruby/#{RUBY_VERSION}",
|
26
|
+
method: :post,
|
27
|
+
content_type: "application/x-www-form-urlencoded",
|
28
|
+
auth_url: "https://acc.qbox.me/oauth2/token",
|
29
|
+
access_key: "",
|
30
|
+
secret_key: "",
|
31
|
+
auto_reconnect: true,
|
32
|
+
max_retry_times: 3,
|
33
|
+
block_size: 1024 * 1024 * 4,
|
34
|
+
chunk_size: 1024 * 256,
|
35
|
+
enable_debug: true,
|
36
|
+
tmpdir: Dir.tmpdir + File::SEPARATOR + "QiniuRuby",
|
37
|
+
multi_region: false,
|
38
|
+
bucket_private: false
|
39
|
+
}.freeze
|
40
|
+
end
|
41
|
+
|
42
|
+
def http_options
|
43
|
+
{
|
44
|
+
rs_host: "http://rs.qiniu.com",
|
45
|
+
rsf_host: "http://rsf.qbox.me",
|
46
|
+
pub_host: "http://pu.qbox.me:10200",
|
47
|
+
eu_host: "http://eu.qbox.me",
|
48
|
+
uc_host: "http://uc.qbox.me",
|
49
|
+
api_host: "http://api.qiniu.com",
|
50
|
+
protocol: :http
|
51
|
+
}.freeze
|
52
|
+
end
|
53
|
+
|
54
|
+
def https_options
|
55
|
+
{
|
56
|
+
rs_host: "https://rs.qbox.me",
|
57
|
+
rsf_host: "https://rsf.qbox.me",
|
58
|
+
pub_host: "https://pu.qbox.me",
|
59
|
+
eu_host: "https://eu.qbox.me",
|
60
|
+
uc_host: "https://uc.qbox.me",
|
61
|
+
api_host: "https://api.qiniu.com",
|
62
|
+
protocol: :https
|
63
|
+
}.freeze
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class Qinium
|
2
|
+
class Configurable < Hash
|
3
|
+
def initialize(defaults = {})
|
4
|
+
defaults.each_pair do |k, v|
|
5
|
+
self[k.to_sym] = v.is_a?(Hash) ? Configurable.new(v) : v
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def []=(k, v)
|
10
|
+
super(k.to_sym, v)
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_missing(method, *args)
|
14
|
+
if method.to_s.end_with?("=")
|
15
|
+
self[method.to_s[0..-2].to_sym] = args[0]
|
16
|
+
else
|
17
|
+
fetch(method.to_sym)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def respond_to_missing?(method)
|
22
|
+
key = method.to_s.end_with?("=") ? method.to_s[0..-2].to_sym : method
|
23
|
+
key?(key)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
class Qinium
|
2
|
+
class HostManager
|
3
|
+
class Cache
|
4
|
+
def initialize
|
5
|
+
@mutex = Mutex.new
|
6
|
+
@hosts = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def read(access_key, bucket, &block)
|
10
|
+
@mutex.synchronize do
|
11
|
+
key = cache_key(access_key, bucket)
|
12
|
+
data = @hosts[key]
|
13
|
+
if data && Time.now.to_i > data["expires_at"]
|
14
|
+
@hosts.delete key
|
15
|
+
data = nil
|
16
|
+
end
|
17
|
+
if data.nil?
|
18
|
+
data = block.call
|
19
|
+
data["expires_at"] = Time.now.to_i + data["ttl"]
|
20
|
+
@hosts[key] = data.freeze
|
21
|
+
end
|
22
|
+
data
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def write(access_key, bucket, hosts)
|
27
|
+
@mutex.synchronize do
|
28
|
+
@hosts[cache_key(access_key, bucket)] = hosts
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def delete(access_key, bucket)
|
33
|
+
@mutex.synchronize do
|
34
|
+
@hosts.delete(cache_key(access_key, bucket))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def cache_key(access_key, bucket)
|
39
|
+
Digest::SHA1.base64digest "#{access_key}-#{bucket}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.cache
|
44
|
+
@cache ||= Cache.new
|
45
|
+
end
|
46
|
+
|
47
|
+
attr_reader :config
|
48
|
+
|
49
|
+
def initialize(config)
|
50
|
+
@config = config
|
51
|
+
end
|
52
|
+
|
53
|
+
def up_host(bucket, opts = {})
|
54
|
+
up_hosts(bucket, opts)[0]
|
55
|
+
end
|
56
|
+
|
57
|
+
def up_hosts(bucket, opts = {})
|
58
|
+
hosts = hosts(bucket)
|
59
|
+
hosts[extract_protocol(opts)]["up"]
|
60
|
+
end
|
61
|
+
|
62
|
+
def hosts(bucket)
|
63
|
+
raise Error, "access_key is missing" if Utils.blank?(access_key)
|
64
|
+
raise Error, "bucket is missing" if Utils.blank?(bucket)
|
65
|
+
|
66
|
+
cache.read(access_key, bucket) do
|
67
|
+
query = Client.to_query(ak: access_key, bucket: bucket)
|
68
|
+
url = "#{config.uc_host}/v1/query?#{query}"
|
69
|
+
http = Client.new(config)
|
70
|
+
code, body, headers = http.get(url)
|
71
|
+
raise APIError.new(code, body, headers) unless Client.success?(code)
|
72
|
+
|
73
|
+
body
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def cache
|
80
|
+
self.class.cache
|
81
|
+
end
|
82
|
+
|
83
|
+
def access_key
|
84
|
+
config.access_key
|
85
|
+
end
|
86
|
+
|
87
|
+
def extract_protocol(opts)
|
88
|
+
(opts[:protocol] || config.protocol).to_s
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
class Qinium
|
2
|
+
class Object
|
3
|
+
include Qinium::Utils
|
4
|
+
|
5
|
+
attr_reader :client
|
6
|
+
|
7
|
+
def initialize(client)
|
8
|
+
@client = client
|
9
|
+
end
|
10
|
+
|
11
|
+
# rubocop: disable Style/SingleLineMethods
|
12
|
+
def config; client.config end
|
13
|
+
def bucket; config.bucket end
|
14
|
+
# rubocop: enable Style/SingleLineMethods
|
15
|
+
|
16
|
+
def mkblk(blk, token:, host: nil)
|
17
|
+
host ||= config.up_host
|
18
|
+
client.post("#{host}/mkblk/#{blk.size}", blk, headers: {
|
19
|
+
"Content-Type" => "application/octet-stream",
|
20
|
+
"Content-Length" => blk.size,
|
21
|
+
"Authorization" => "UpToken #{token}"
|
22
|
+
})
|
23
|
+
end
|
24
|
+
|
25
|
+
def mkfile(token:, file_size:, blocks:, key: nil, fname: nil, mime_type: nil, user_vars: nil)
|
26
|
+
url = config.up_host
|
27
|
+
url += "/mkfile/#{file_size}"
|
28
|
+
url += "/key/#{urlsafe_base64_encode(key)}" if key
|
29
|
+
url += "/fname/#{urlsafe_base64_encode(fname)}" if fname
|
30
|
+
url += "/mimeType/#{urlsafe_base64_encode(mime_type)}" if mime_type
|
31
|
+
url += "/x:user-var/#{urlsafe_base64_encode(user_vars)}" if user_vars
|
32
|
+
client.post(url, blocks.join(","), headers: {
|
33
|
+
"Context-Type" => "text/plain",
|
34
|
+
"Authorization" => "UpToken #{token}"
|
35
|
+
})
|
36
|
+
end
|
37
|
+
|
38
|
+
def delete(key, bucket: self.bucket)
|
39
|
+
client.management_post(config.rs_host + "/delete/" + Utils.encode_entry_uri(
|
40
|
+
bucket, key
|
41
|
+
))
|
42
|
+
end
|
43
|
+
|
44
|
+
def list(bucket: self.bucket, marker: "", limit: 1000, prefix: "", delimiter: "")
|
45
|
+
query_string = to_query_string(bucket: bucket, marker: marker, limit: limit, prefix: prefix,
|
46
|
+
delimiter: delimiter)
|
47
|
+
client.management_get(config.rsf_host + "/list?" + query_string)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
class Qinium
|
2
|
+
class PutPolicy
|
3
|
+
include Qinium::Utils
|
4
|
+
|
5
|
+
DEFAULT_AUTH_SECONDS = 300
|
6
|
+
|
7
|
+
PARAMS = {
|
8
|
+
# 字符串类型参数
|
9
|
+
scope: "scope",
|
10
|
+
is_prefixal_scope: "isPrefixalScope",
|
11
|
+
save_key: "saveKey",
|
12
|
+
end_user: "endUser",
|
13
|
+
return_url: "returnUrl",
|
14
|
+
return_body: "returnBody",
|
15
|
+
callback_url: "callbackUrl",
|
16
|
+
callback_host: "callbackHost",
|
17
|
+
callback_body: "callbackBody",
|
18
|
+
callback_body_type: "callbackBodyType",
|
19
|
+
persistent_ops: "persistentOps",
|
20
|
+
persistent_notify_url: "persistentNotifyUrl",
|
21
|
+
persistent_pipeline: "persistentPipeline",
|
22
|
+
|
23
|
+
# 数值类型参数
|
24
|
+
deadline: "deadline",
|
25
|
+
insert_only: "insertOnly",
|
26
|
+
fsize_min: "fsizeMin",
|
27
|
+
fsize_limit: "fsizeLimit",
|
28
|
+
detect_mime: "detectMime",
|
29
|
+
mime_limit: "mimeLimit",
|
30
|
+
uphosts: "uphosts",
|
31
|
+
global: "global",
|
32
|
+
delete_after_days: "deleteAfterDays",
|
33
|
+
file_type: "fileType"
|
34
|
+
}
|
35
|
+
|
36
|
+
PARAMS.each_pair do |key, _|
|
37
|
+
attr_accessor key
|
38
|
+
end
|
39
|
+
|
40
|
+
attr_reader :config, :bucket, :key
|
41
|
+
|
42
|
+
def initialize(config, bucket: nil, key: nil, expires_in: DEFAULT_AUTH_SECONDS)
|
43
|
+
@config = config
|
44
|
+
scope!(bucket || config.bucket, key)
|
45
|
+
expires_in! expires_in
|
46
|
+
end
|
47
|
+
|
48
|
+
def scope!(bucket, key = nil)
|
49
|
+
@bucket = bucket
|
50
|
+
@key = key
|
51
|
+
|
52
|
+
@scope = if key.nil?
|
53
|
+
# 新增语义,文件已存在则失败
|
54
|
+
bucket
|
55
|
+
else
|
56
|
+
# 覆盖语义,文件已存在则直接覆盖
|
57
|
+
"#{bucket}:#{key}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def expires_in!(seconds)
|
62
|
+
@deadline = Time.now.to_i + seconds
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_json(*_args)
|
66
|
+
args = {}
|
67
|
+
|
68
|
+
PARAMS.each_pair do |key, fld|
|
69
|
+
val = __send__(key)
|
70
|
+
args[fld] = val unless val.nil?
|
71
|
+
end
|
72
|
+
|
73
|
+
args.to_json
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_token
|
77
|
+
access_key = config.access_key
|
78
|
+
secret_key = config.secret_key
|
79
|
+
|
80
|
+
### 生成待签名字符串
|
81
|
+
encoded_put_policy = urlsafe_base64_encode(to_json)
|
82
|
+
|
83
|
+
### 生成数字签名
|
84
|
+
sign = Auth.calculate_hmac_sha1_digest(secret_key, encoded_put_policy)
|
85
|
+
encoded_sign = urlsafe_base64_encode(sign)
|
86
|
+
|
87
|
+
### 生成上传授权凭证
|
88
|
+
"#{access_key}:#{encoded_sign}:#{encoded_put_policy}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/lib/qinium/utils.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
class Qinium
|
2
|
+
module Utils
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def urlsafe_base64_encode(content)
|
6
|
+
Base64.encode64(content).strip.gsub("+", "-").gsub("/", "_").gsub(/\r?\n/, "")
|
7
|
+
end
|
8
|
+
|
9
|
+
def urlsafe_base64_decode(encoded_content)
|
10
|
+
Base64.decode64 encoded_content.gsub("_", "/").gsub("-", "+")
|
11
|
+
end
|
12
|
+
|
13
|
+
def encode_entry_uri(bucket, key)
|
14
|
+
entry_uri = bucket + ":" + key
|
15
|
+
urlsafe_base64_encode(entry_uri)
|
16
|
+
end
|
17
|
+
|
18
|
+
def blank?(obj)
|
19
|
+
case obj
|
20
|
+
when String then obj.strip.empty?
|
21
|
+
when NilClass then true
|
22
|
+
when Array, Hash then obj.size.zero?
|
23
|
+
else
|
24
|
+
false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_query_string(hash)
|
29
|
+
args = []
|
30
|
+
hash.keys.sort_by(&:to_s).each do |key|
|
31
|
+
value = hash[key]
|
32
|
+
next if value.nil?
|
33
|
+
|
34
|
+
args.push("#{CGI.escape(key.to_s)}=#{CGI.escape(value.to_s)}")
|
35
|
+
end
|
36
|
+
args.join("&")
|
37
|
+
end
|
38
|
+
|
39
|
+
def logger
|
40
|
+
@logger ||= Logger.new(STDOUT, level: Qinium.logger_level)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/qinium.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
class Qinium
|
2
|
+
require "logger"
|
3
|
+
require "base64"
|
4
|
+
require "tempfile"
|
5
|
+
require "openssl"
|
6
|
+
require "digest/sha1"
|
7
|
+
require "cgi"
|
8
|
+
require "net/http"
|
9
|
+
require "json"
|
10
|
+
require "qinium/version"
|
11
|
+
require "qinium/errors"
|
12
|
+
require "qinium/utils"
|
13
|
+
require "qinium/configurable"
|
14
|
+
require "qinium/config"
|
15
|
+
require "qinium/client"
|
16
|
+
require "qinium/host_manager"
|
17
|
+
require "qinium/auth"
|
18
|
+
require "qinium/object"
|
19
|
+
require "qinium/put_policy"
|
20
|
+
|
21
|
+
@logger_level = :info
|
22
|
+
class << self
|
23
|
+
attr_accessor :logger_level
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_reader :config
|
27
|
+
|
28
|
+
def initialize(options = {})
|
29
|
+
@config = Config.new(options)
|
30
|
+
end
|
31
|
+
|
32
|
+
def object
|
33
|
+
@object ||= Object.new(client)
|
34
|
+
end
|
35
|
+
|
36
|
+
def client
|
37
|
+
@client ||= Client.new(config)
|
38
|
+
end
|
39
|
+
end
|
data/qinium.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/qinium/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "qinium"
|
7
|
+
spec.version = Qinium::VERSION
|
8
|
+
spec.authors = ["xiaohui"]
|
9
|
+
spec.email = ["xiaohui@tanmer.com"]
|
10
|
+
|
11
|
+
spec.summary = "Qiniu Cloud Storage SDK for multi-tenant system"
|
12
|
+
spec.description = "Qiniu Cloud Storage SDK for multi-tenant system, can call API with multiple accounts"
|
13
|
+
spec.homepage = "https://github.com/xiaohui-zhangxh/qinium"
|
14
|
+
spec.license = "MIT"
|
15
|
+
spec.required_ruby_version = ">= 2.6.0"
|
16
|
+
|
17
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
18
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
19
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/CHANGELOG.md"
|
20
|
+
|
21
|
+
# Specify which files should be added to the gem when it is released.
|
22
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
23
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
24
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
25
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
spec.require_paths = ["lib"]
|
30
|
+
end
|
data/sig/qinium.rbs
ADDED
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: qinium
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- xiaohui
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-08-10 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Qiniu Cloud Storage SDK for multi-tenant system, can call API with multiple
|
14
|
+
accounts
|
15
|
+
email:
|
16
|
+
- xiaohui@tanmer.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- ".rspec"
|
22
|
+
- ".rubocop.yml"
|
23
|
+
- CHANGELOG.md
|
24
|
+
- Gemfile
|
25
|
+
- Gemfile.lock
|
26
|
+
- LICENSE.txt
|
27
|
+
- README.md
|
28
|
+
- Rakefile
|
29
|
+
- lib/qinium.rb
|
30
|
+
- lib/qinium/auth.rb
|
31
|
+
- lib/qinium/client.rb
|
32
|
+
- lib/qinium/config.rb
|
33
|
+
- lib/qinium/configurable.rb
|
34
|
+
- lib/qinium/errors.rb
|
35
|
+
- lib/qinium/host_manager.rb
|
36
|
+
- lib/qinium/object.rb
|
37
|
+
- lib/qinium/put_policy.rb
|
38
|
+
- lib/qinium/utils.rb
|
39
|
+
- lib/qinium/version.rb
|
40
|
+
- qinium.gemspec
|
41
|
+
- sig/qinium.rbs
|
42
|
+
homepage: https://github.com/xiaohui-zhangxh/qinium
|
43
|
+
licenses:
|
44
|
+
- MIT
|
45
|
+
metadata:
|
46
|
+
homepage_uri: https://github.com/xiaohui-zhangxh/qinium
|
47
|
+
source_code_uri: https://github.com/xiaohui-zhangxh/qinium
|
48
|
+
changelog_uri: https://github.com/xiaohui-zhangxh/qinium/CHANGELOG.md
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options: []
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: 2.6.0
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
requirements: []
|
64
|
+
rubygems_version: 3.3.7
|
65
|
+
signing_key:
|
66
|
+
specification_version: 4
|
67
|
+
summary: Qiniu Cloud Storage SDK for multi-tenant system
|
68
|
+
test_files: []
|