staticd 0.0.1
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/bin/staticd +7 -0
- data/lib/rack/auth/hmac.rb +81 -0
- data/lib/rack/request_time.rb +19 -0
- data/lib/staticd.rb +7 -0
- data/lib/staticd/api.rb +356 -0
- data/lib/staticd/app.rb +130 -0
- data/lib/staticd/cache_engine.rb +45 -0
- data/lib/staticd/cli.rb +70 -0
- data/lib/staticd/config.rb +115 -0
- data/lib/staticd/database.rb +41 -0
- data/lib/staticd/datastore.rb +64 -0
- data/lib/staticd/datastores/local.rb +48 -0
- data/lib/staticd/datastores/s3.rb +63 -0
- data/lib/staticd/domain_generator.rb +42 -0
- data/lib/staticd/http_cache.rb +65 -0
- data/lib/staticd/http_server.rb +197 -0
- data/lib/staticd/json_request.rb +15 -0
- data/lib/staticd/json_response.rb +29 -0
- data/lib/staticd/models/base.rb +43 -0
- data/lib/staticd/models/domain_name.rb +17 -0
- data/lib/staticd/models/release.rb +20 -0
- data/lib/staticd/models/resource.rb +19 -0
- data/lib/staticd/models/route.rb +19 -0
- data/lib/staticd/models/site.rb +36 -0
- data/lib/staticd/models/staticd_config.rb +71 -0
- data/lib/staticd/public/jquery-1.11.1.min.js +4 -0
- data/lib/staticd/public/main.css +61 -0
- data/lib/staticd/public/main.js +15 -0
- data/lib/staticd/version.rb +3 -0
- data/lib/staticd/views/main.haml +9 -0
- data/lib/staticd/views/setup.haml +47 -0
- data/lib/staticd/views/welcome.haml +92 -0
- data/lib/staticd_utils/archive.rb +80 -0
- data/lib/staticd_utils/file_size.rb +26 -0
- data/lib/staticd_utils/gli_object.rb +14 -0
- data/lib/staticd_utils/memory_file.rb +36 -0
- data/lib/staticd_utils/sitemap.rb +100 -0
- data/lib/staticd_utils/tar.rb +43 -0
- metadata +300 -0
data/lib/staticd/app.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
require "rack"
|
2
|
+
require "rack/request_time"
|
3
|
+
|
4
|
+
require "staticd/config"
|
5
|
+
require "staticd/database"
|
6
|
+
require "staticd/datastore"
|
7
|
+
require "staticd/api"
|
8
|
+
require "staticd/http_cache"
|
9
|
+
require "staticd/http_server"
|
10
|
+
|
11
|
+
module Staticd
|
12
|
+
|
13
|
+
# Staticd App.
|
14
|
+
#
|
15
|
+
# This class manage the app initialization and runtime.
|
16
|
+
#
|
17
|
+
# Example:
|
18
|
+
# app = Staticd::App.new(config)
|
19
|
+
# app.run
|
20
|
+
class App
|
21
|
+
|
22
|
+
# Initialize the Staticd app.
|
23
|
+
#
|
24
|
+
# General configuration:
|
25
|
+
# * environment: the app environment (test, development or production)
|
26
|
+
# * domain: base to generate per app sub-domain
|
27
|
+
# * public_port: port used to generate application and endpoint url,
|
28
|
+
# (default to 80)
|
29
|
+
# * database: database url to store resources metadata
|
30
|
+
# * datastore: datastore url to store resources
|
31
|
+
# * host: host to listen to
|
32
|
+
# * port: port to listen to
|
33
|
+
#
|
34
|
+
# API service configuration:
|
35
|
+
# * api: enable the API service
|
36
|
+
# * access_id: HMAC authentication access ID for the API service
|
37
|
+
# * secret_key: HMAC authentication secret key for the API service
|
38
|
+
#
|
39
|
+
# HTTP service configuration:
|
40
|
+
# * http: enable the HTTP service
|
41
|
+
# * http_cache: folder where resources are cached
|
42
|
+
def initialize(config)
|
43
|
+
@config = config
|
44
|
+
@config[:public_port] ||= "80"
|
45
|
+
require_settings(:environment, :domain, :database, :datastore)
|
46
|
+
|
47
|
+
env = @config[:environment]
|
48
|
+
puts "Starting Staticd in #{env} environment." unless env == "test"
|
49
|
+
display_current_config if env == "development"
|
50
|
+
|
51
|
+
init_database
|
52
|
+
init_datastore
|
53
|
+
end
|
54
|
+
|
55
|
+
# Start the application.
|
56
|
+
def run
|
57
|
+
require_settings(:host, :port)
|
58
|
+
require_settings(:http_cache) if @config[:http]
|
59
|
+
require_settings(:access_id, :secret_key) if @config[:api]
|
60
|
+
|
61
|
+
routes = {}
|
62
|
+
routes["/"] = build_http_service if @config[:http]
|
63
|
+
routes["/api/#{Staticd::API::VERSION}"] = build_api_service if @config[:api]
|
64
|
+
router = Rack::URLMap.new(routes)
|
65
|
+
|
66
|
+
Rack::Server.start(
|
67
|
+
Host: @config[:host],
|
68
|
+
Port: @config[:port],
|
69
|
+
app: router,
|
70
|
+
environment: @config[:environment]
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def require_settings(*settings)
|
77
|
+
settings.each do |setting|
|
78
|
+
unless @config.key?(setting) && !@config[setting].nil?
|
79
|
+
raise "Missing '#{setting}' setting"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def display_current_config
|
85
|
+
puts "Configuration:"
|
86
|
+
puts "* Database: #{@config[:database]}"
|
87
|
+
puts "* Datastore: #{@config[:datastore]}"
|
88
|
+
|
89
|
+
if Staticd::Config[:api]
|
90
|
+
puts "* Host: #{@config[:host]}"
|
91
|
+
puts "* Port: #{@config[:port]}"
|
92
|
+
puts "* Domain: #{@config[:domain]}"
|
93
|
+
puts "* Public Port: #{@config[:public_port]}"
|
94
|
+
puts "* Access ID: #{@config[:access_id]}"
|
95
|
+
puts "* Secret Key: #{@config[:secret_key]}"
|
96
|
+
end
|
97
|
+
|
98
|
+
if Staticd::Config[:http]
|
99
|
+
puts "* HTTP cache: #{@config[:http_cache]}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def init_database
|
104
|
+
Staticd::Database.setup(@config[:environment], @config[:database])
|
105
|
+
end
|
106
|
+
|
107
|
+
def init_datastore
|
108
|
+
Staticd::Datastore.setup(@config[:datastore])
|
109
|
+
end
|
110
|
+
|
111
|
+
def build_api_service
|
112
|
+
api_service = Staticd::API.new(@config)
|
113
|
+
|
114
|
+
# Bind the API service with the HMAC middleware.
|
115
|
+
raise "No access ID provided" unless @config[:access_id]
|
116
|
+
raise "No secret_key provided" unless @config[:secret_key]
|
117
|
+
Rack::Auth::HMAC.new(
|
118
|
+
api_service, except: Staticd::API::PUBLIC_URI
|
119
|
+
) do |access_id|
|
120
|
+
@config[:secret_key] if access_id == @config[:access_id].to_s
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def build_http_service
|
125
|
+
http_service = Staticd::HTTPServer.new(@config[:http_cache])
|
126
|
+
cache_middleware = Staticd::HTTPCache.new(@config[:http_cache], http_service)
|
127
|
+
Rack::RequestTime.new(cache_middleware)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "open-uri"
|
2
|
+
require "sendfile"
|
3
|
+
|
4
|
+
module Staticd
|
5
|
+
|
6
|
+
# Class to manage HTTP resources caching.
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
# cache_engine = CacheEngine.new("/tmp/cache")
|
10
|
+
# unless cache.cached?("/index.html")
|
11
|
+
# cache_engine.cache("/index.html", "http://storage.tld/0000000")
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# TODO: add a purge method based on file's atime attribute
|
15
|
+
class CacheEngine
|
16
|
+
|
17
|
+
def initialize(http_root)
|
18
|
+
@http_root = http_root
|
19
|
+
check_cache_directory
|
20
|
+
end
|
21
|
+
|
22
|
+
def cache(resource_path, resource_url)
|
23
|
+
open(resource_url, "rb") do |resource|
|
24
|
+
FileUtils.mkdir_p(File.dirname(local_path(resource_path)))
|
25
|
+
File.open(local_path(resource_path), "w+") do |file|
|
26
|
+
resource.each { |chunk| file.write(chunk) }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def cached?(resource_path)
|
32
|
+
File.exist?(local_path(resource_path))
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def local_path(resource_path)
|
38
|
+
@http_root + resource_path
|
39
|
+
end
|
40
|
+
|
41
|
+
def check_cache_directory
|
42
|
+
FileUtils.mkdir_p(@http_root) unless File.directory?(@http_root)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/staticd/cli.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require "staticd_utils/gli_object"
|
2
|
+
require "staticd"
|
3
|
+
|
4
|
+
module Staticd
|
5
|
+
class CLI
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@gli = GLIObject.new
|
9
|
+
@gli.program_desc("Staticd HTTP and API server")
|
10
|
+
@gli.version(Staticd::VERSION)
|
11
|
+
@gli.on_error { |exception| raise exception }
|
12
|
+
build_commands
|
13
|
+
end
|
14
|
+
|
15
|
+
def run(*args)
|
16
|
+
@gli.run(*args)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def build_commands
|
22
|
+
build_command_server
|
23
|
+
end
|
24
|
+
|
25
|
+
def build_command_server
|
26
|
+
@gli.desc("Start the staticd API and HTTP services")
|
27
|
+
@gli.command(:server) do |c|
|
28
|
+
c.switch([:api], desc: "enable the API service", default_value: true)
|
29
|
+
c.switch([:http], desc: "enable the HTTP service", default_value: true)
|
30
|
+
c.flag(
|
31
|
+
[:environment],
|
32
|
+
desc: "application environment",
|
33
|
+
default_value: :development
|
34
|
+
)
|
35
|
+
c.flag(
|
36
|
+
[:domain],
|
37
|
+
desc: "base to generate per app sub-domain"
|
38
|
+
)
|
39
|
+
c.flag(
|
40
|
+
[:public_port],
|
41
|
+
desc: "port used to generate application and endpoint url"
|
42
|
+
)
|
43
|
+
c.flag([:access_id], desc: "HMAC auth access id for the API service")
|
44
|
+
c.flag([:secret_key], desc: "HMAC auth secret key for the API service")
|
45
|
+
c.flag([:database], desc: "URL for the database")
|
46
|
+
c.flag([:datastore], desc: "URL for the datastore")
|
47
|
+
c.flag(
|
48
|
+
[:http_cache],
|
49
|
+
desc: "directory path where HTTP resources are cached",
|
50
|
+
default_value: "/var/cache/staticd"
|
51
|
+
)
|
52
|
+
c.flag([:host], desc: "address to listen to", default_value: "0.0.0.0")
|
53
|
+
c.flag([:port], desc: "port to listen to", default_value: 80)
|
54
|
+
c.flag([:config], desc: "load a config file")
|
55
|
+
c.action do |global_options, options,args|
|
56
|
+
|
57
|
+
# Load configuration from command line options, environment variables
|
58
|
+
# options and config file.
|
59
|
+
Staticd::Config << options
|
60
|
+
Staticd::Config.load_env
|
61
|
+
Staticd::Config.load_file(options[:config]) if options[:config]
|
62
|
+
|
63
|
+
# Initialize and start the Staticd app.
|
64
|
+
app = Staticd::App.new(Staticd::Config)
|
65
|
+
app.run
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require "singleton"
|
2
|
+
|
3
|
+
module Staticd
|
4
|
+
|
5
|
+
# Manage Staticd Configuration.
|
6
|
+
#
|
7
|
+
# Can load configuration from a hash, from environment variables with the
|
8
|
+
# STATICD_ prefix or from a config file in yaml format.
|
9
|
+
#
|
10
|
+
# Once loaded,configuration is available from anywhere in the app using
|
11
|
+
# Staticd::Config[:setting].
|
12
|
+
class Config
|
13
|
+
include Singleton
|
14
|
+
|
15
|
+
# Load configuration from environment variables.
|
16
|
+
#
|
17
|
+
# Example:
|
18
|
+
# ENV["STATICD_FOO"] = "bar"
|
19
|
+
# Staticd::Config.load_env
|
20
|
+
# Staticd::Config[:foo]
|
21
|
+
# # => "bar"
|
22
|
+
def self.load_env
|
23
|
+
settings = {}
|
24
|
+
env = ENV.select { |name, value| name =~ /^STATICD_/ }
|
25
|
+
env.each do |name, value|
|
26
|
+
setting = name[/^STATICD_(.*)/, 1].downcase.to_sym
|
27
|
+
settings[setting] = value
|
28
|
+
end
|
29
|
+
instance << settings
|
30
|
+
end
|
31
|
+
|
32
|
+
# Load configuration from a YAML file.
|
33
|
+
#
|
34
|
+
# The configuration file can contain ERB code.
|
35
|
+
#
|
36
|
+
# Example (config file)
|
37
|
+
# ---
|
38
|
+
# foo: bar
|
39
|
+
#
|
40
|
+
# Example:
|
41
|
+
# Staticd::Config.load_file("/etc/staticd/staticd.yml")
|
42
|
+
# Staticd::Config[:foo]
|
43
|
+
# # => "bar"
|
44
|
+
def self.load_file(config_file)
|
45
|
+
content = File.read(config_file)
|
46
|
+
erb = ERB.new(content)
|
47
|
+
settings = YAML.load(erb.result)
|
48
|
+
instance << settings
|
49
|
+
end
|
50
|
+
|
51
|
+
# Push settings into Staticd global configuration.
|
52
|
+
#
|
53
|
+
# String setting keys are converted to symbols.
|
54
|
+
#
|
55
|
+
# Example:
|
56
|
+
# Staticd::Config << {"foo" => bar}
|
57
|
+
# Staticd::Config[:foo]
|
58
|
+
# # => "bar"
|
59
|
+
def self.<<(settings)
|
60
|
+
instance << settings
|
61
|
+
end
|
62
|
+
|
63
|
+
# Same as << method.
|
64
|
+
def self.[]=(setting, value)
|
65
|
+
instance << {setting => value}
|
66
|
+
end
|
67
|
+
|
68
|
+
# Get a setting value from the Staticd global configuration.
|
69
|
+
def self.[](setting)
|
70
|
+
instance[setting]
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.key?(setting_name)
|
74
|
+
instance.key?(setting_name)
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.to_s
|
78
|
+
instance.to_s
|
79
|
+
end
|
80
|
+
|
81
|
+
def initialize
|
82
|
+
@settings = {}
|
83
|
+
end
|
84
|
+
|
85
|
+
def <<(settings)
|
86
|
+
settings = hash_symbolize_keys(settings)
|
87
|
+
mutex.synchronize { @settings.merge!(settings) }
|
88
|
+
end
|
89
|
+
|
90
|
+
def [](setting)
|
91
|
+
mutex.synchronize { @settings.key?(setting) ? @settings[setting] : nil }
|
92
|
+
end
|
93
|
+
|
94
|
+
def key?(setting_name)
|
95
|
+
mutex.synchronize { @settings.key?(setting_name) }
|
96
|
+
end
|
97
|
+
|
98
|
+
def to_s
|
99
|
+
mutex.synchronize { @settings.to_s }
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
|
104
|
+
def hash_symbolize_keys(hash)
|
105
|
+
hash.keys.each do |key|
|
106
|
+
hash[(key.to_sym rescue key) || key] = hash.delete(key)
|
107
|
+
end
|
108
|
+
hash
|
109
|
+
end
|
110
|
+
|
111
|
+
def mutex
|
112
|
+
@mutex ||= Mutex.new
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "data_mapper"
|
2
|
+
|
3
|
+
# Load models.
|
4
|
+
Dir["#{File.dirname(__FILE__)}/models/*.rb"].each do |model_library|
|
5
|
+
require model_library
|
6
|
+
end
|
7
|
+
|
8
|
+
module Staticd
|
9
|
+
module Database
|
10
|
+
|
11
|
+
# Initialize the database.
|
12
|
+
#
|
13
|
+
# It support the test, development and production environment.
|
14
|
+
# Database logger is silent in test environment, verbose in development
|
15
|
+
# environment, and only displaying errors in production.
|
16
|
+
def self.setup(environment, database_url)
|
17
|
+
raise "No environment given for the database" unless environment
|
18
|
+
raise "No database_url given" unless database_url
|
19
|
+
|
20
|
+
environment = environment.to_sym
|
21
|
+
|
22
|
+
log_enabled, destination, level =
|
23
|
+
case environment
|
24
|
+
when :development
|
25
|
+
[true, '$stdout', :debug]
|
26
|
+
when :production
|
27
|
+
[true, '$stderr', :error]
|
28
|
+
else
|
29
|
+
[false]
|
30
|
+
end
|
31
|
+
|
32
|
+
if log_enabled
|
33
|
+
DataMapper::Logger.new(eval(destination), level)
|
34
|
+
end
|
35
|
+
|
36
|
+
DataMapper.setup(:default, database_url)
|
37
|
+
DataMapper.finalize
|
38
|
+
environment == :test ? DataMapper.auto_migrate! : DataMapper.auto_upgrade!
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require "singleton"
|
2
|
+
require "uri"
|
3
|
+
|
4
|
+
# Load datastores libraries.
|
5
|
+
Dir["#{File.dirname(__FILE__)}/datastores/*.rb"].each do |datastore_library|
|
6
|
+
require datastore_library
|
7
|
+
end
|
8
|
+
|
9
|
+
module Staticd
|
10
|
+
|
11
|
+
# Load the corresponding datastore driver from an URL.
|
12
|
+
#
|
13
|
+
# This class use an URL to choose wich datastore library to use.
|
14
|
+
# It create a datastore instance with the correct driver and proxies its
|
15
|
+
# calls to it.
|
16
|
+
# It use the URL scheme to guess wich datastore library to use.
|
17
|
+
#
|
18
|
+
# Example:
|
19
|
+
# Staticd::Datastore.setup("s3://[...]") # Staticd::Datastores::S3
|
20
|
+
# Staticd::Datastore.setup("local:/[...]") # Staticd::Datastores::Local
|
21
|
+
# Staticd::Datastore.put(file_path)
|
22
|
+
class Datastore
|
23
|
+
include Singleton
|
24
|
+
|
25
|
+
def self.setup(url)
|
26
|
+
instance.setup(url)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.put(file_path)
|
30
|
+
instance.put(file_path)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.exist?(file_path)
|
34
|
+
instance.exist?(file_path)
|
35
|
+
end
|
36
|
+
|
37
|
+
def setup(url)
|
38
|
+
@uri = URI(url)
|
39
|
+
end
|
40
|
+
|
41
|
+
def put(file_path)
|
42
|
+
datastore.put(file_path)
|
43
|
+
end
|
44
|
+
|
45
|
+
def exist?(file_path)
|
46
|
+
datastore.exist?(file_path)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def datastoreClass
|
52
|
+
@datastoreClass ||= Datastores.const_get(@uri.scheme.capitalize)
|
53
|
+
end
|
54
|
+
|
55
|
+
def datastore
|
56
|
+
@datastore ||= datastoreClass.new(
|
57
|
+
host: @uri.host,
|
58
|
+
path: @uri.path,
|
59
|
+
username: @uri.user,
|
60
|
+
password: @uri.password
|
61
|
+
)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|