midb 1.0.5 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|