diff --git a/public/index.html b/public/index.html
new file mode 100644
index 0000000..e986e50
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,78 @@
+
+
+
+ electronic parts database explorer
+
+
+
+
+
+
+
+
+
+
+
+ name |
+ description |
+ stock |
+
+
+
+
+
+
+
diff --git a/server.rb b/server.rb
new file mode 100755
index 0000000..d497060
--- /dev/null
+++ b/server.rb
@@ -0,0 +1,118 @@
+#!/usr/bin/env ruby
+# encoding: utf-8
+# ruby: 3.0.2
+=begin
+server to query part database
+
+to install sinatra
+sudo pacman -S ruby-sinatra ruby-webrick
+pikaur -S ruby-mysql2
+=end
+require 'set'
+require 'mysql2'
+require 'json'
+require 'sinatra'
+
+DEBUG = true
+PARTS_LIMIT = 100
+CREDENTIALS = "credentials.json"
+raise "database information #{CREDENTIALS} do not exist" unless File.file? CREDENTIALS
+
+# open server
+configure do
+ if DEBUG then
+ set :show_exceptions, true
+ set :logging, true
+ else
+ set :show_exceptions, false
+ set :environment, :production
+ set :logging, false
+ end
+ set :protection, :except => :json_csrf
+
+ set :bind, 'localhost'
+ set :port, 4244
+ set :public_folder, "public"
+ set :static, true
+end
+
+before do
+ response.headers["Access-Control-Allow-Origin"] = "*"
+ response.headers["Access-Control-Allow-Headers"] = "Content-Type"
+ if request.request_method == 'OPTIONS'
+ response.headers["Access-Control-Allow-Methods"] = "GET,POST"
+ halt 200
+ end
+
+ # all replies are only JSON
+ content_type 'application/json'
+ # open database
+ credentials = {}
+ JSON.parse(IO.read(CREDENTIALS)).each {|key,value| credentials[key.to_sym] = value}
+ Mysql2::Client.default_query_options.merge!(:as => :hash)
+ @db = Mysql2::Client.new(credentials)
+end
+
+after do
+ response.headers["Access-Control-Allow-Origin"] = "*"
+ response.headers["Access-Control-Allow-Headers"] = "Content-Type"
+end
+
+get '/' do
+ redirect to('/index.html')
+end
+
+def get_part_by_id(id)
+ statement = @db.prepare("SELECT part.name, part.description, part.datasheet, manufacturer.name AS manufacturer, package.name AS package, part.pincount AS pincount, part.page AS page, part.family AS family FROM part LEFT JOIN package ON package.id = part.package LEFT JOIN manufacturer ON manufacturer.id = part.manufacturer WHERE part.id = ?")
+ part = statement.execute(id, :as => :hash).to_a[0]
+ return nil unless part
+ # merge with family info
+ while part and part["family"] do
+ family = statement.execute(part["family"]).to_a[0]
+ part["family"] = nil # reset info
+ break unless family
+ family.each do |k,v|
+ part[k] ||= v
+ end
+ end
+ # clean up
+ delete = ["family"]
+ delete.each do |k|
+ part.delete k
+ end
+ return part
+end
+
+def get_part_by_name(name)
+ statement = @db.prepare("SELECT id FROM part WHERE part.name = ?")
+ id = statement.execute(name).to_a[0]
+ if id then
+ return get_part_by_id(id["id"])
+ else
+ return nil
+ end
+end
+
+get '/part/:name' do
+ part = get_part_by_name(params['name'])
+ halt 404 unless part
+ part.to_json
+end
+
+get '/search/:terms' do
+ terms = params['terms'].split(" ")
+ terms.keep_if {|term| term.length >= 3}
+ ids = Set.new
+ part_statement = @db.prepare("SELECT id FROM part WHERE name LIKE ?")
+ terms.each do |term|
+ part_statement.execute("%#{term}%").each do |row|
+ ids << row["id"]
+ end
+ end
+ puts ids
+ parts = ids.collect {|id| get_part_by_id(id)}
+ parts.compact!
+ parts = parts[0, PARTS_LIMIT]
+ parts.sort! {|x,y| x["name"] <=> y["name"]}
+ parts.to_json
+end