midb 1.0.5 → 1.1.0
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 +4 -4
- data/bin/midb +6 -3
- data/lib/midb/dbengine_model.rb +78 -74
- data/lib/midb/errors_view.rb +26 -27
- data/lib/midb/hooks.rb +13 -0
- data/lib/midb/security_controller.rb +33 -31
- data/lib/midb/server_controller.rb +228 -221
- data/lib/midb/server_model.rb +248 -232
- data/lib/midb/server_view.rb +97 -87
- data/lib/midb/serverengine_controller.rb +113 -105
- data/lib/midb.rb +2 -1
- metadata +3 -2
data/lib/midb/server_view.rb
CHANGED
@@ -1,98 +1,108 @@
|
|
1
|
-
require 'midb/server_controller'
|
2
1
|
module MIDB
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
end
|
2
|
+
module Interface
|
3
|
+
# View that handles everything from the server
|
4
|
+
class Server
|
7
5
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
return {"error" => {"errno" => errno, "msg" => msg}}
|
12
|
-
end
|
6
|
+
def self.success()
|
7
|
+
puts "Success!"
|
8
|
+
end
|
13
9
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
10
|
+
# Return a JSON error response
|
11
|
+
#
|
12
|
+
# @param errno [Fixnum] Error number.
|
13
|
+
# @param msg [String] Error message.
|
14
|
+
def self.json_error(errno, msg)
|
15
|
+
return {"error" => {"errno" => errno, "msg" => msg}}
|
20
16
|
end
|
21
|
-
end
|
22
17
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
18
|
+
# Shows the files being served
|
19
|
+
#
|
20
|
+
# @param cnf [Array<String>] The configuration from the server.
|
21
|
+
def self.show_serving(cnf)
|
22
|
+
puts "The follow JSON files are being served as APIs:"
|
23
|
+
cnf["serves"].each do |serv|
|
24
|
+
puts "- #{serv}"
|
25
|
+
end
|
26
|
+
end
|
28
27
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
when :start then "Server started on port #{info}. Listening for connections..."
|
34
|
-
when :incoming_request then "> Incoming request from #{info}."
|
35
|
-
when :request then ">> Request method: #{info[0]}\n>>> Endpoint: #{info[1]}"
|
36
|
-
when :match_json then ">> The request matched a JSON file: #{info}.json\n>> Creating response..."
|
37
|
-
when :response then ">> Sending JSON response (RAW):\n#{info}"
|
38
|
-
when :success then "> Successfully managed this request!"
|
39
|
-
when :not_found then "> Invalid endpoint - sending a 404 error."
|
40
|
-
when :auth_required then ">> Authentication required. Checking for the HTTP header..."
|
41
|
-
when :no_auth then ">> No authentication header - sending a 401 error."
|
42
|
-
when :auth_success then ">> Successfully authenticated the request."
|
43
|
-
when :bootstrap then "> Successfully bootstraped!"
|
44
|
-
end
|
45
|
-
puts msg
|
46
|
-
end
|
28
|
+
# Notice that the server has been stopped.
|
29
|
+
def self.server_stopped()
|
30
|
+
puts "The server has been successfully stopped!"
|
31
|
+
end
|
47
32
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
33
|
+
# Send some info
|
34
|
+
#
|
35
|
+
# @param what [Symbol] What to show the information for.
|
36
|
+
# @param info [Array<String>] Extra information needed for the message.
|
37
|
+
def self.info(what, info=nil)
|
38
|
+
msg = case what
|
39
|
+
when :start then "Server started on port #{info}. Listening for connections..."
|
40
|
+
when :incoming_request then "> Incoming request from #{info}."
|
41
|
+
when :request then ">> Request method: #{info[0]}\n>>> Endpoint: #{info[1]}"
|
42
|
+
when :match_json then ">> The request matched a JSON file: #{info}.json\n>> Creating response..."
|
43
|
+
when :response then ">> Sending JSON response (RAW):\n#{info}"
|
44
|
+
when :success then "> Successfully managed this request!"
|
45
|
+
when :not_found then "> Invalid endpoint - sending a 404 error."
|
46
|
+
when :auth_required then ">> Authentication required. Checking for the HTTP header..."
|
47
|
+
when :no_auth then ">> No authentication header - sending a 401 error."
|
48
|
+
when :auth_success then ">> Successfully authenticated the request."
|
49
|
+
when :bootstrap then "> Successfully bootstraped!"
|
50
|
+
end
|
51
|
+
puts msg
|
52
|
+
end
|
53
|
+
|
54
|
+
# Output some config
|
55
|
+
#
|
56
|
+
# @param what [Symbol] What to show the config for.
|
57
|
+
# @param cnf [Array<String>] The array for the config.
|
58
|
+
def self.out_config(what, cnf)
|
59
|
+
msg = case what
|
60
|
+
when :dbengine then "Database engine: #{cnf['dbengine']}."
|
61
|
+
when :dbhost then "Database server host: #{cnf['dbhost']}."
|
62
|
+
when :dbport then "Database server port: #{cnf['dbport']}."
|
63
|
+
when :dbuser then "Database server user: #{cnf['dbuser']}."
|
64
|
+
when :dbpassword then "Database server password: #{cnf['dbpassword']}."
|
65
|
+
when :apikey then "Private API key: #{cnf['apikey']}"
|
66
|
+
else "Error??"
|
67
|
+
end
|
68
|
+
puts msg
|
69
|
+
end
|
62
70
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
71
|
+
# Shows the help
|
72
|
+
#
|
73
|
+
# @param what [Symbol] What to show the help for.
|
74
|
+
def self.help(what)
|
75
|
+
case what
|
76
|
+
when :list
|
77
|
+
puts "midb has several commands that you can use. For detailed information, see `midb help command`."
|
78
|
+
puts " "
|
79
|
+
puts "bootstrap\tCreate the basic files and directories that midb needs to be ran in a folder."
|
80
|
+
puts "set\tModify this project's settings. See the detailed help for a list of options."
|
81
|
+
puts "serve\tServes a JSON file - creates an API endpoint."
|
82
|
+
puts "unserve\tStops serving a JSON file - the endpoint is no longer valid."
|
83
|
+
puts "start\tStarts an API server. See detailed help for more."
|
84
|
+
when :bootstrap
|
85
|
+
puts "This command creates the `.midb.yaml` config file, and the `db` and `json` directories if they don't exist."
|
86
|
+
puts "You must bootstrap before running any other commands."
|
87
|
+
when :set
|
88
|
+
puts "Sets config options. If no value is given, it shows the current value."
|
89
|
+
puts "db:host\tHost name of the database (for MySQL)"
|
90
|
+
puts "db:user\tUsername for the database (for MySQL)"
|
91
|
+
puts "db:password\tPassword for the database (for MySQL)"
|
92
|
+
puts "db:engine\t(sqlite3, mysql) Changes the database engine."
|
93
|
+
puts "api:key\tChanges the private API key, used for authentication over HTTP."
|
94
|
+
when :serve
|
95
|
+
puts "This command will create an API endpoint pointing to a JSON file in the json/ directory."
|
96
|
+
puts "It will support GET, POST, PUT and DELETE requests."
|
97
|
+
puts "For detailed information on how to format your file, see the GitHub README and/or wiki."
|
98
|
+
when :unserve
|
99
|
+
puts "Stops serving a JSON file under the json/ directory."
|
100
|
+
when :start
|
101
|
+
puts "Starts the server. You must run the serve/unserve commands beforehand, so to set some endpoints."
|
102
|
+
puts "Options:"
|
103
|
+
puts "db:DATABASE\tSets DATABASE as the database where to get the data. Mandatory."
|
104
|
+
puts "port:PORT\tSets PORT as the port where the server will listen to. Default: 8081."
|
105
|
+
end
|
96
106
|
end
|
97
107
|
end
|
98
108
|
end
|
@@ -11,11 +11,11 @@ require 'json'
|
|
11
11
|
require 'sqlite3'
|
12
12
|
|
13
13
|
module MIDB
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
module API
|
15
|
+
# @author unrar
|
16
|
+
# This class handles runs the server engine using sockets and a loop.
|
17
|
+
class Engine
|
18
|
+
# Attribute declaration here
|
19
19
|
# @!attribute config
|
20
20
|
# @return [Hash] Contains the project's configuration, saved in .midb.yaml
|
21
21
|
# @!attribute db
|
@@ -23,127 +23,135 @@ module MIDB
|
|
23
23
|
# @!attribute http_status
|
24
24
|
# @return [String] HTTP status code and string representation for the header
|
25
25
|
attr_accessor :config, :db, :http_status
|
26
|
-
end
|
27
26
|
|
27
|
+
# Constructor
|
28
|
+
#
|
29
|
+
# @param db [String] Database to which the server will bind.
|
30
|
+
# @param stat [Fixnum] HTTP Status
|
31
|
+
# @param cnf [Hash] Config from the server controller.
|
32
|
+
def initialize(db, stat, cnf)
|
33
|
+
@config = cnf
|
34
|
+
@db = db
|
35
|
+
@http_status = stat
|
36
|
+
end
|
28
37
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
break if line[0] == ""
|
51
|
-
headers[line[0].chop] = line[1].strip
|
52
|
-
end
|
53
|
-
data = socket.read(headers["Content-Length"].to_i)
|
38
|
+
# Starts the server on a given port (default: 8081)
|
39
|
+
#
|
40
|
+
# @param port [Fixnum] Port to which the server will listen.
|
41
|
+
def start(port=8081)
|
42
|
+
serv = TCPServer.new("localhost", port)
|
43
|
+
MIDB::Interface::Server.info(:start, port)
|
44
|
+
|
45
|
+
# Manage the requests
|
46
|
+
loop do
|
47
|
+
socket = serv.accept
|
48
|
+
MIDB::Interface::Server.info(:incoming_request, socket.addr[3])
|
49
|
+
|
50
|
+
request = self.parse_request(socket.gets)
|
51
|
+
|
52
|
+
# Get a hash with the headers
|
53
|
+
headers = {}
|
54
|
+
while line = socket.gets.split(' ', 2)
|
55
|
+
break if line[0] == ""
|
56
|
+
headers[line[0].chop] = line[1].strip
|
57
|
+
end
|
58
|
+
data = socket.read(headers["Content-Length"].to_i)
|
54
59
|
|
55
60
|
|
56
|
-
|
57
|
-
|
61
|
+
MIDB::Interface::Server.info(:request, request)
|
62
|
+
response_json = Hash.new()
|
58
63
|
|
59
|
-
|
60
|
-
|
61
|
-
|
64
|
+
# Endpoint syntax: ["", FILE, ID, (ACTION)]
|
65
|
+
endpoint = request[1].split("/")
|
66
|
+
ep_file = endpoint[1]
|
62
67
|
|
63
|
-
|
64
|
-
|
68
|
+
method = request[0]
|
69
|
+
endpoints = [] # Valid endpoints
|
65
70
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
+
# Load the JSON served files
|
72
|
+
@config["serves"].each do |js|
|
73
|
+
# The filename is a valid endpoint
|
74
|
+
endpoints.push File.basename(js, ".*")
|
75
|
+
end
|
71
76
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
77
|
+
# Load the endpoints
|
78
|
+
found = false
|
79
|
+
endpoints.each do |ep|
|
80
|
+
if ep_file == ep
|
81
|
+
found = true
|
82
|
+
MIDB::Interface::Server.info(:match_json, ep)
|
83
|
+
# Create the model
|
84
|
+
dbop = MIDB::API::Model.new(ep, @db, self)
|
85
|
+
# Analyze the request and pass it to the model
|
86
|
+
if method == "GET"
|
87
|
+
case endpoint.length
|
88
|
+
when 2
|
89
|
+
# No ID has been specified. Return all the entries
|
90
|
+
# Pass it to the model and get the JSON
|
91
|
+
response_json = dbop.get_all_entries().to_json
|
92
|
+
when 3
|
93
|
+
# An ID has been specified. Should it exist, return all of its entries.
|
94
|
+
response_json = dbop.get_entries(endpoint[2]).to_json
|
95
|
+
end
|
96
|
+
else
|
97
|
+
# An action has been specified. We're going to need HTTP authentification here.
|
98
|
+
MIDB::Interface::Server.info(:auth_required)
|
92
99
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
100
|
+
if (not headers.has_key? "Authentication") ||
|
101
|
+
(not MIDB::API::Security.check?(headers["Authentication"], data, @config["apikey"]))
|
102
|
+
@http_status = "401 Unauthorized"
|
103
|
+
response_json = MIDB::Interface::Server.json_error(401, "Unauthorized").to_json
|
104
|
+
MIDB::Interface::Server.info(:no_auth)
|
98
105
|
|
99
|
-
else
|
100
|
-
MIDB::ServerView.info(:auth_success)
|
101
|
-
if method == "POST"
|
102
|
-
response_json = MIDB::ServerModel.post(@db, ep, data).to_json
|
103
106
|
else
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
elsif method == "PUT"
|
108
|
-
response_json = MIDB::ServerModel.put(@db, ep, endpoint[2], data).to_json
|
109
|
-
end
|
107
|
+
MIDB::Interface::Server.info(:auth_success)
|
108
|
+
if method == "POST"
|
109
|
+
response_json = dbop.post(data).to_json
|
110
110
|
else
|
111
|
-
|
112
|
-
|
111
|
+
if endpoint.length >= 3
|
112
|
+
if method == "DELETE"
|
113
|
+
response_json = dbop.delete(endpoint[2]).to_json
|
114
|
+
elsif method == "PUT"
|
115
|
+
response_json = dbop.put(endpoint[2], data).to_json
|
116
|
+
end
|
117
|
+
else
|
118
|
+
@http_status = "404 Not Found"
|
119
|
+
response_json = MIDB::Interface::Server.json_error(404, "Must specify an ID.").to_json
|
120
|
+
end
|
113
121
|
end
|
114
122
|
end
|
115
123
|
end
|
124
|
+
MIDB::Interface::Server.info(:response, response_json)
|
125
|
+
# Return the results via HTTP
|
126
|
+
socket.print "HTTP/1.1 #{@http_status}\r\n" +
|
127
|
+
"Content-Type: text/json\r\n" +
|
128
|
+
"Content-Length: #{response_json.size}\r\n" +
|
129
|
+
"Connection: close\r\n"
|
130
|
+
socket.print "\r\n"
|
131
|
+
socket.print response_json
|
132
|
+
socket.print "\r\n"
|
133
|
+
MIDB::Interface::Server.info(:success)
|
116
134
|
end
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
135
|
+
end
|
136
|
+
unless found
|
137
|
+
MIDB::Interface::Server.info(:not_found)
|
138
|
+
response = MIDB::Interface::Server.json_error(404, "Invalid API endpoint.").to_json
|
139
|
+
|
140
|
+
socket.print "HTTP/1.1 404 Not Found\r\n" +
|
141
|
+
"Content-Type: text/json\r\n" +
|
142
|
+
"Content-Length: #{response.size}\r\n" +
|
143
|
+
"Connection: close\r\n"
|
125
144
|
socket.print "\r\n"
|
126
|
-
|
145
|
+
socket.print response
|
127
146
|
end
|
128
147
|
end
|
129
|
-
unless found
|
130
|
-
MIDB::ServerView.info(:not_found)
|
131
|
-
response = MIDB::ServerView.json_error(404, "Invalid API endpoint.").to_json
|
132
|
-
|
133
|
-
socket.print "HTTP/1.1 404 Not Found\r\n" +
|
134
|
-
"Content-Type: text/json\r\n" +
|
135
|
-
"Content-Length: #{response.size}\r\n" +
|
136
|
-
"Connection: close\r\n"
|
137
|
-
socket.print "\r\n"
|
138
|
-
socket.print response
|
139
|
-
end
|
140
148
|
end
|
141
|
-
end
|
142
149
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
150
|
+
# Method: parse_request
|
151
|
+
# Parses an HTTP requests and returns an array [method, uri]
|
152
|
+
def parse_request(req)
|
153
|
+
[req.split(" ")[0], req.split(" ")[1]]
|
154
|
+
end
|
147
155
|
end
|
148
156
|
end
|
149
157
|
end
|
data/lib/midb.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: midb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- unrar
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-11-
|
11
|
+
date: 2015-11-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mysql2
|
@@ -102,6 +102,7 @@ files:
|
|
102
102
|
- lib/midb.rb
|
103
103
|
- lib/midb/dbengine_model.rb
|
104
104
|
- lib/midb/errors_view.rb
|
105
|
+
- lib/midb/hooks.rb
|
105
106
|
- lib/midb/security_controller.rb
|
106
107
|
- lib/midb/server_controller.rb
|
107
108
|
- lib/midb/server_model.rb
|