diff options
-rw-r--r-- | src/guff.cr | 204 | ||||
-rw-r--r-- | src/guff/asset-mime-type.cr | 20 | ||||
-rw-r--r-- | src/guff/config.cr | 137 | ||||
-rw-r--r-- | src/guff/iso8601.cr | 3 | ||||
-rw-r--r-- | src/guff/paged-result-set.cr | 23 | ||||
-rw-r--r-- | src/guff/password.cr | 16 |
6 files changed, 205 insertions, 198 deletions
diff --git a/src/guff.cr b/src/guff.cr index 9791288..e0f97ad 100644 --- a/src/guff.cr +++ b/src/guff.cr @@ -1,11 +1,14 @@ -require "option_parser" require "http/server" require "ecr/macros" require "json" require "yaml" require "secure_random" -require "crypto/bcrypt" require "sqlite3" + +module Guff + VERSION = "0.1.0" +end + require "./guff/*" private macro define_lazy_getters(hash) @@ -43,201 +46,6 @@ private macro api_method_dispatch(modules) end module Guff - class Config - property :mode, :env, :host, :port, :data_dir, :system_dir - - DEFAULTS = { - mode: "help", - env: "production", - host: "127.0.0.1", - port: "8989", - data_dir: "./data", - system_dir: "/usr/local/share/guff", - } - - def initialize - @mode = DEFAULTS[:mode] as String - @env = (ENV["GUFF_ENVIRONMENT"]? || DEFAULTS[:env]) as String - @host = (ENV["GUFF_HOST"]? || DEFAULTS[:host]) as String - @port = (ENV["GUFF_PORT"]? || DEFAULTS[:port]) as String - @data_dir = (ENV["GUFF_DATA_DIR"]? || DEFAULTS[:data_dir]) as String - @system_dir = (ENV["GUFF_SYSTEM_DIR"]? || DEFAULTS[:system_dir]) as String - end - - VALID_MODES = %w{init run help} - - def mode=(mode : String) - raise "unknown mode: \"#{mode}\"" unless VALID_MODES.includes?(mode) - @mode = mode - end - - VALID_ENVS = %w{development production} - - def env=(env : String) - raise "unknown environment: \"#{env}\"" unless VALID_ENVS.includes?(env) - @env = env - end - - def port=(port : String) - val = port.to_i - raise "invalid port: #{port}" unless val > 0 && val < 65535 - @port = port - end - - def system_dir=(dir : String) - raise "missing system dir: \"#{dir}\"" unless Dir.exists?(dir) - @system_dir = dir - end - - def db_path - File.join(@data_dir, "guff.db") - end - - def self.parse( - app : String, - args : Array(String) - ) : Config - r = Config.new - - raise "missing mode" unless args.size > 0 - - # get mode - r.mode = case mode = args.shift - when "-h", "--help" - "help" - else - mode - end - - # parse arguments - p = OptionParser.parse(args) do |p| - p.banner = "Usage: #{app} [mode] <args>" - - p.separator - p.separator("Run Options:") - - p.on( - "-H HOST", "--host HOST", - "TCP host (defaults to \"#{DEFAULTS[:host]}\")" - ) do |arg| - r.host = arg - end - - p.on( - "-p PORT", "--port PORT", - "TCP port (defaults to \"#{DEFAULTS[:port]}\")" - ) do |arg| - r.port = arg - end - - p.separator - p.separator("Directory Options:") - - p.on( - "-D DIR", "--data-dir DIR", - "Data directory (defaults to \"#{DEFAULTS[:data_dir]}\")" - ) do |arg| - r.data_dir = arg - end - - p.on( - "-S DIR", "--system-dir DIR", - "Guff system directory (defaults to \"#{DEFAULTS[:system_dir]}\")" - ) do |arg| - r.system_dir = arg - end - - p.separator - p.separator("Development Options:") - - p.on( - "-E ENV", "--environment ENV", - "Environment (defaults to \"#{DEFAULTS[:env]})\"" - ) do |arg| - r.env = arg - end - - p.separator - p.separator("Other Options:") - - p.on("-h", "--help", "Print usage.") do - r.mode = "help" - end - end - - case r.mode - when "init" - # shortcut for -D parameter - r.data_dir = args.shift if args.size > 0 - when "help" - # print help - puts p - end - - # return config - r - end - end - - ISO8601 = ::Time::Format.new("%Y-%m-%dT%H:%M:%SZ") - - module MimeType - TYPES = { - ".js" => "text/javascript; charset=utf-8", - ".css" => "text/css; charset=utf-8", - ".html" => "text/html; charset=utf-8", - ".png" => "image/png", - ".jpeg" => "image/jpeg", - ".jpg" => "image/jpeg", - ".otf" => "application/vnd.ms-opentype", - ".eot" => "application/vnd.ms-fontobject", - ".svg" => "image/svg+xml", - ".ttf" => "application/x-font-ttf", - ".woff" => "application/font-woff", - ".woff2" => "application/font-woff", - } - - def self.from_path(path : String) : String - TYPES[File.extname(path)]? || "application/octet-stream" - end - end - - module Password - def self.create(password : String) : String - Crypto::Bcrypt::Password.create(password).to_s - end - - def self.test(hash : String, password : String) : Bool - Crypto::Bcrypt::Password.new(hash) == password - end - - def self.random_password - SecureRandom.base64(6 + rand(6)).strip.gsub(/\=+$/, "") - end - end - - class PagedResultSet - def initialize( - @page : Int32, - @num_rows : Int64, - @limit : Int32, - @rows : Array(Hash(String, String)) - ) - end - - def to_json(io) - { - "meta": { - "page": @page, - "num_rows": @num_rows, - "num_pages": (1.0 * @num_rows / @limit).ceil - }, - - "rows": @rows - }.to_json(io) - end - end - module Models abstract class Model def initialize(@context : Context) @@ -2287,7 +2095,7 @@ module Guff # not cached, set code and send headers context.response.headers["x-frame-options"] = "SAMEORIGIN" context.response.status_code = 200 - context.response.content_type = MimeType.from_path(abs_path) + context.response.content_type = AssetMimeType.from_path(abs_path) context.response.content_length = File.size(abs_path) context.response.headers["etag"] = etag diff --git a/src/guff/asset-mime-type.cr b/src/guff/asset-mime-type.cr new file mode 100644 index 0000000..ee3826c --- /dev/null +++ b/src/guff/asset-mime-type.cr @@ -0,0 +1,20 @@ +module Guff::AssetMimeType + TYPES = { + ".js" => "text/javascript; charset=utf-8", + ".css" => "text/css; charset=utf-8", + ".html" => "text/html; charset=utf-8", + ".png" => "image/png", + ".jpeg" => "image/jpeg", + ".jpg" => "image/jpeg", + ".otf" => "application/vnd.ms-opentype", + ".eot" => "application/vnd.ms-fontobject", + ".svg" => "image/svg+xml", + ".ttf" => "application/x-font-ttf", + ".woff" => "application/font-woff", + ".woff2" => "application/font-woff", + } + + def self.from_path(path : String) : String + TYPES[File.extname(path)]? || "application/octet-stream" + end +end diff --git a/src/guff/config.cr b/src/guff/config.cr new file mode 100644 index 0000000..534e796 --- /dev/null +++ b/src/guff/config.cr @@ -0,0 +1,137 @@ +require "option_parser" + +class Guff::Config + property :mode, :env, :host, :port, :data_dir, :system_dir + + DEFAULTS = { + mode: "help", + env: "production", + host: "127.0.0.1", + port: "8989", + data_dir: "./data", + system_dir: "/usr/local/share/guff", + } + + def initialize + @mode = DEFAULTS[:mode] as String + @env = (ENV["GUFF_ENVIRONMENT"]? || DEFAULTS[:env]) as String + @host = (ENV["GUFF_HOST"]? || DEFAULTS[:host]) as String + @port = (ENV["GUFF_PORT"]? || DEFAULTS[:port]) as String + @data_dir = (ENV["GUFF_DATA_DIR"]? || DEFAULTS[:data_dir]) as String + @system_dir = (ENV["GUFF_SYSTEM_DIR"]? || DEFAULTS[:system_dir]) as String + end + + VALID_MODES = %w{init run help} + + def mode=(mode : String) + raise "unknown mode: \"#{mode}\"" unless VALID_MODES.includes?(mode) + @mode = mode + end + + VALID_ENVS = %w{development production} + + def env=(env : String) + raise "unknown environment: \"#{env}\"" unless VALID_ENVS.includes?(env) + @env = env + end + + def port=(port : String) + val = port.to_i + raise "invalid port: #{port}" unless val > 0 && val < 65535 + @port = port + end + + def system_dir=(dir : String) + raise "missing system dir: \"#{dir}\"" unless Dir.exists?(dir) + @system_dir = dir + end + + def db_path + File.join(@data_dir, "guff.db") + end + + def self.parse( + app : String, + args : Array(String) + ) : Config + r = Config.new + + raise "missing mode" unless args.size > 0 + + # get mode + r.mode = case mode = args.shift + when "-h", "--help" + "help" + else + mode + end + + # parse arguments + p = OptionParser.parse(args) do |p| + p.banner = "Usage: #{app} [mode] <args>" + + p.separator + p.separator("Run Options:") + + p.on( + "-H HOST", "--host HOST", + "TCP host (defaults to \"#{DEFAULTS[:host]}\")" + ) do |arg| + r.host = arg + end + + p.on( + "-p PORT", "--port PORT", + "TCP port (defaults to \"#{DEFAULTS[:port]}\")" + ) do |arg| + r.port = arg + end + + p.separator + p.separator("Directory Options:") + + p.on( + "-D DIR", "--data-dir DIR", + "Data directory (defaults to \"#{DEFAULTS[:data_dir]}\")" + ) do |arg| + r.data_dir = arg + end + + p.on( + "-S DIR", "--system-dir DIR", + "Guff system directory (defaults to \"#{DEFAULTS[:system_dir]}\")" + ) do |arg| + r.system_dir = arg + end + + p.separator + p.separator("Development Options:") + + p.on( + "-E ENV", "--environment ENV", + "Environment (defaults to \"#{DEFAULTS[:env]})\"" + ) do |arg| + r.env = arg + end + + p.separator + p.separator("Other Options:") + + p.on("-h", "--help", "Print usage.") do + r.mode = "help" + end + end + + case r.mode + when "init" + # shortcut for -D parameter + r.data_dir = args.shift if args.size > 0 + when "help" + # print help + puts p + end + + # return config + r + end +end diff --git a/src/guff/iso8601.cr b/src/guff/iso8601.cr new file mode 100644 index 0000000..7dab18f --- /dev/null +++ b/src/guff/iso8601.cr @@ -0,0 +1,3 @@ +module Guff + ISO8601 = ::Time::Format.new("%Y-%m-%dT%H:%M:%SZ") +end diff --git a/src/guff/paged-result-set.cr b/src/guff/paged-result-set.cr new file mode 100644 index 0000000..2db3619 --- /dev/null +++ b/src/guff/paged-result-set.cr @@ -0,0 +1,23 @@ +require "json" + +class Guff::PagedResultSet + def initialize( + @page : Int32, + @num_rows : Int64, + @limit : Int32, + @rows : Array(Hash(String, String)) + ) + end + + def to_json(io) + { + "meta": { + "page": @page, + "num_rows": @num_rows, + "num_pages": (1.0 * @num_rows / @limit).ceil + }, + + "rows": @rows + }.to_json(io) + end +end diff --git a/src/guff/password.cr b/src/guff/password.cr new file mode 100644 index 0000000..2fc8e60 --- /dev/null +++ b/src/guff/password.cr @@ -0,0 +1,16 @@ +require "secure_random" +require "crypto/bcrypt" + +module Guff::Password + def self.create(password : String) : String + Crypto::Bcrypt::Password.create(password).to_s + end + + def self.test(hash : String, password : String) : Bool + Crypto::Bcrypt::Password.new(hash) == password + end + + def self.random_password + SecureRandom.base64(6 + rand(6)).strip.gsub(/\=+$/, "") + end +end |