poto 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.rubocop.yml +4 -0
- data/.travis.yml +3 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Dockerfile +21 -0
- data/Gemfile +14 -0
- data/LICENSE.txt +21 -0
- data/Procfile +1 -0
- data/README.md +97 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/docker-compose.yml +16 -0
- data/exe/poto-aws-s3 +29 -0
- data/exe/poto-google-cloud-storage +34 -0
- data/lib/poto.rb +11 -0
- data/lib/poto/api.rb +62 -0
- data/lib/poto/app.rb +11 -0
- data/lib/poto/ext/open_uri.rb +7 -0
- data/lib/poto/file_repository/aws/s3.rb +32 -0
- data/lib/poto/file_repository/aws/s3/client.rb +33 -0
- data/lib/poto/file_repository/aws/s3/file_collection_mapper.rb +36 -0
- data/lib/poto/file_repository/aws/s3/file_mapper.rb +37 -0
- data/lib/poto/file_repository/dsl.rb +25 -0
- data/lib/poto/file_repository/file.rb +5 -0
- data/lib/poto/file_repository/file_collection.rb +5 -0
- data/lib/poto/file_repository/google/cloud/storage.rb +34 -0
- data/lib/poto/file_repository/google/cloud/storage/file_collection_mapper.rb +37 -0
- data/lib/poto/file_repository/google/cloud/storage/file_mapper.rb +39 -0
- data/lib/poto/file_repository/proxy.rb +27 -0
- data/lib/poto/image_proxy.rb +45 -0
- data/lib/poto/representers/file_collection_representer.rb +22 -0
- data/lib/poto/representers/file_representer.rb +19 -0
- data/lib/poto/services/download.rb +24 -0
- data/lib/poto/services/file_cache.rb +28 -0
- data/lib/poto/services/resize.rb +23 -0
- data/lib/poto/version.rb +3 -0
- data/poto.gemspec +40 -0
- data/public/ajax-loader.gif +0 -0
- data/public/index.html +150 -0
- metadata +244 -0
data/lib/poto/app.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'poto/file_repository/aws/s3/client'
|
3
|
+
require 'poto/file_repository/aws/s3/file_collection_mapper'
|
4
|
+
|
5
|
+
module Poto
|
6
|
+
module FileRepository
|
7
|
+
module AWS
|
8
|
+
class S3
|
9
|
+
attr_reader :bucket, :client
|
10
|
+
|
11
|
+
def initialize(bucket:, client: Client.new(bucket))
|
12
|
+
@bucket = bucket
|
13
|
+
@client = client
|
14
|
+
end
|
15
|
+
|
16
|
+
def url(id)
|
17
|
+
client.url(id)
|
18
|
+
end
|
19
|
+
|
20
|
+
def all(prefix:, page:, per_page:)
|
21
|
+
objects = client.objects(
|
22
|
+
page: page,
|
23
|
+
per_page: per_page,
|
24
|
+
prefix: prefix
|
25
|
+
)
|
26
|
+
|
27
|
+
FileCollectionMapper.new(objects).call
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
|
3
|
+
module Poto
|
4
|
+
module FileRepository
|
5
|
+
module AWS
|
6
|
+
class S3
|
7
|
+
class Client
|
8
|
+
attr_reader :bucket
|
9
|
+
|
10
|
+
def initialize(bucket)
|
11
|
+
@bucket = bucket
|
12
|
+
end
|
13
|
+
|
14
|
+
def objects(prefix:, page:, per_page:)
|
15
|
+
Aws::S3::Client.new.list_objects(
|
16
|
+
bucket: bucket,
|
17
|
+
marker: page,
|
18
|
+
max_keys: per_page,
|
19
|
+
prefix: prefix
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
def url(key)
|
24
|
+
Aws::S3::Object.new(
|
25
|
+
bucket,
|
26
|
+
key
|
27
|
+
).presigned_url(:get, expires_in: 3600)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'poto/file_repository/aws/s3/file_mapper'
|
2
|
+
require 'poto/file_repository/file_collection'
|
3
|
+
|
4
|
+
module Poto
|
5
|
+
module FileRepository
|
6
|
+
module AWS
|
7
|
+
class S3
|
8
|
+
class FileCollectionMapper
|
9
|
+
attr_reader :objects
|
10
|
+
|
11
|
+
def initialize(objects)
|
12
|
+
@objects = objects
|
13
|
+
end
|
14
|
+
|
15
|
+
def call
|
16
|
+
FileCollection.new(files, page, next_page)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def files
|
22
|
+
objects.contents.map { |object| FileMapper.new(object).call }
|
23
|
+
end
|
24
|
+
|
25
|
+
def page
|
26
|
+
objects.marker
|
27
|
+
end
|
28
|
+
|
29
|
+
def next_page
|
30
|
+
files.last.try(:name)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
require 'poto/file_repository/file'
|
4
|
+
|
5
|
+
module Poto
|
6
|
+
module FileRepository
|
7
|
+
module AWS
|
8
|
+
class S3
|
9
|
+
class FileMapper
|
10
|
+
attr_reader :object
|
11
|
+
|
12
|
+
def initialize(object)
|
13
|
+
@object = object
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
File.new(id, name, size)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def name
|
23
|
+
object.key
|
24
|
+
end
|
25
|
+
|
26
|
+
def size
|
27
|
+
object.size
|
28
|
+
end
|
29
|
+
|
30
|
+
def id
|
31
|
+
object.key
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Poto
|
2
|
+
module FileRepository
|
3
|
+
class Proxy
|
4
|
+
module DSL
|
5
|
+
module ClassMethods
|
6
|
+
def option(name)
|
7
|
+
define_method(name) do |value|
|
8
|
+
tap do
|
9
|
+
options[name] = value
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.included(base)
|
16
|
+
base.extend(ClassMethods)
|
17
|
+
end
|
18
|
+
|
19
|
+
def options
|
20
|
+
@__options ||= {}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'google/cloud/storage'
|
3
|
+
|
4
|
+
require 'poto/file_repository/google/cloud/storage/file_collection_mapper'
|
5
|
+
|
6
|
+
module Poto
|
7
|
+
module FileRepository
|
8
|
+
module Google
|
9
|
+
module Cloud
|
10
|
+
class Storage
|
11
|
+
attr_reader :client
|
12
|
+
|
13
|
+
def initialize(client:)
|
14
|
+
@client = client
|
15
|
+
end
|
16
|
+
|
17
|
+
def url(id)
|
18
|
+
client.file(id).signed_url
|
19
|
+
end
|
20
|
+
|
21
|
+
def all(prefix:, page:, per_page:)
|
22
|
+
files = client.files(
|
23
|
+
prefix: prefix,
|
24
|
+
token: page,
|
25
|
+
max: per_page
|
26
|
+
)
|
27
|
+
|
28
|
+
FileCollectionMapper.new(files, page).call
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
require 'poto/file_repository/file_collection'
|
4
|
+
require 'poto/file_repository/google/cloud/storage/file_mapper'
|
5
|
+
|
6
|
+
module Poto
|
7
|
+
module FileRepository
|
8
|
+
module Google
|
9
|
+
module Cloud
|
10
|
+
class Storage
|
11
|
+
class FileCollectionMapper
|
12
|
+
attr_reader :page
|
13
|
+
|
14
|
+
def initialize(files, page)
|
15
|
+
@files = files
|
16
|
+
@page = page
|
17
|
+
end
|
18
|
+
|
19
|
+
def call
|
20
|
+
FileCollection.new(files, page, next_page)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def files
|
26
|
+
@files.map { |file| FileMapper.new(file).call }
|
27
|
+
end
|
28
|
+
|
29
|
+
def next_page
|
30
|
+
@files.token
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
require 'poto/file_repository/file'
|
4
|
+
|
5
|
+
module Poto
|
6
|
+
module FileRepository
|
7
|
+
module Google
|
8
|
+
module Cloud
|
9
|
+
class Storage
|
10
|
+
class FileMapper
|
11
|
+
attr_reader :file
|
12
|
+
|
13
|
+
def initialize(file)
|
14
|
+
@file = file
|
15
|
+
end
|
16
|
+
|
17
|
+
def call
|
18
|
+
File.new(id, name, size)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def name
|
24
|
+
file.name
|
25
|
+
end
|
26
|
+
|
27
|
+
def size
|
28
|
+
file.size
|
29
|
+
end
|
30
|
+
|
31
|
+
def id
|
32
|
+
file.name
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'poto/file_repository/dsl'
|
2
|
+
|
3
|
+
module Poto
|
4
|
+
module FileRepository
|
5
|
+
class Proxy
|
6
|
+
include DSL
|
7
|
+
|
8
|
+
option :per_page
|
9
|
+
option :page
|
10
|
+
option :prefix
|
11
|
+
|
12
|
+
attr_reader :backend
|
13
|
+
|
14
|
+
def initialize(backend)
|
15
|
+
@backend = backend
|
16
|
+
end
|
17
|
+
|
18
|
+
def url(id)
|
19
|
+
backend.url(id)
|
20
|
+
end
|
21
|
+
|
22
|
+
def all
|
23
|
+
backend.all(options)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
require 'tmpdir'
|
3
|
+
|
4
|
+
require 'poto/services/download'
|
5
|
+
require 'poto/services/file_cache'
|
6
|
+
require 'poto/services/resize'
|
7
|
+
|
8
|
+
module Poto
|
9
|
+
class ImageProxy < Sinatra::Base
|
10
|
+
set :cache_path, Dir.tmpdir
|
11
|
+
|
12
|
+
helpers do
|
13
|
+
def src
|
14
|
+
URI(params['src'].gsub(%r{\A\/\/}, 'http://'))
|
15
|
+
end
|
16
|
+
|
17
|
+
def width
|
18
|
+
params['width'].to_i
|
19
|
+
end
|
20
|
+
|
21
|
+
def height
|
22
|
+
params['height'].to_i
|
23
|
+
end
|
24
|
+
|
25
|
+
def cache(key, &set)
|
26
|
+
Services::FileCache.new(path: settings.cache_path).cache(key, &set)
|
27
|
+
end
|
28
|
+
|
29
|
+
def download(uri)
|
30
|
+
Services::Download.new(file: Tempfile.new(settings.cache_path), uri: uri).call
|
31
|
+
end
|
32
|
+
|
33
|
+
def resize(path, height, width)
|
34
|
+
Services::Resize.new(path: path, height: height, width: width).call
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
get('/') do
|
39
|
+
src_path = cache(src.path) { download(src) }
|
40
|
+
dst_path = cache("#{src_path}#{width}x#{height}") { resize(src_path, height, width) }
|
41
|
+
|
42
|
+
send_file(dst_path)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'roar/representer'
|
2
|
+
require 'roar/json'
|
3
|
+
require 'roar/json/hal'
|
4
|
+
require 'poto/representers/file_representer'
|
5
|
+
|
6
|
+
module Poto
|
7
|
+
module FileCollectionRepresenter
|
8
|
+
include Roar::JSON::HAL
|
9
|
+
include Roar::Hypermedia
|
10
|
+
include Grape::Roar::Representer
|
11
|
+
|
12
|
+
collection :files, extend: FileRepresenter, embedded: true
|
13
|
+
|
14
|
+
link :self do |opts, helpers = opts[:env]['api.endpoint']|
|
15
|
+
helpers.url_for opts, '/files', page: helpers.page
|
16
|
+
end
|
17
|
+
|
18
|
+
link :next do |opts, helpers = opts[:env]['api.endpoint']|
|
19
|
+
helpers.url_for opts, '/files', page: next_page, per_page: helpers.per_page if next_page
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'roar/representer'
|
3
|
+
require 'roar/json'
|
4
|
+
require 'roar/json/hal'
|
5
|
+
|
6
|
+
module Poto
|
7
|
+
module FileRepresenter
|
8
|
+
include Roar::JSON::HAL
|
9
|
+
include Roar::Hypermedia
|
10
|
+
include Grape::Roar::Representer
|
11
|
+
|
12
|
+
property :name
|
13
|
+
property :size
|
14
|
+
|
15
|
+
link :file do |opts, helpers = opts[:env]['api.endpoint']|
|
16
|
+
helpers.url_for opts, '/files/' + Base64.urlsafe_encode64(id)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'poto/ext/open_uri'
|
3
|
+
|
4
|
+
module Poto
|
5
|
+
module Services
|
6
|
+
class Download
|
7
|
+
attr_reader :file, :uri
|
8
|
+
|
9
|
+
def initialize(file:, uri:)
|
10
|
+
@file = file
|
11
|
+
@uri = uri
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
open(uri) do |src|
|
16
|
+
file.write(src.read)
|
17
|
+
end
|
18
|
+
|
19
|
+
file.close
|
20
|
+
file.path
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|