aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/guff.cr204
-rw-r--r--src/guff/asset-mime-type.cr20
-rw-r--r--src/guff/config.cr137
-rw-r--r--src/guff/iso8601.cr3
-rw-r--r--src/guff/paged-result-set.cr23
-rw-r--r--src/guff/password.cr16
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