diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..29c3998 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +.gitignore export-ignore +.gitattributes export-ignore \ No newline at end of file diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..f903ad5 --- /dev/null +++ b/Gemfile @@ -0,0 +1,10 @@ +source 'https://rubygems.org' + +ruby '2.3.2' + +gem 'tilt', '<2.0' +gem 'camping' +gem 'activerecord' +gem 'sqlite3' +gem 'markaby' +gem 'thin' diff --git a/app/controllers.rb b/app/controllers.rb new file mode 100755 index 0000000..bfd230a --- /dev/null +++ b/app/controllers.rb @@ -0,0 +1,115 @@ +#!/usr/bin/env ruby + +module Drugstore::Controllers + class Index < R '/' + def get + @cart = 0 + unless @state.cart.blank? + @state.cart.each do |product_id, quantity| + @cart = @cart + quantity.to_i + end + end + @products = Product.all + render :index + end + + def post + if( ( @input.has_key?( "product_id" ) ) && ( @input.has_key?( "quantity" ) ) ) + @state.cart ||= {} + if @state.cart.has_key?( @input.product_id ) + @state.cart[ @input.product_id.to_i ] = @state.cart[ @input.product_id.to_i ].to_i + @input.quantity.to_i + else + @state.cart[ @input.product_id.to_i ] = @input.quantity.to_i + end + @state.cart[ @input.product_id.to_i ] = 10 if @state.cart[ @input.product_id.to_i ] > 10 + @state.info = ' <= Product successfully added.' + end + redirect Index + end + end + + class Cart < R '/cart' + def get + unless @state.cart.blank? + @products = [] + @sum = 0 + @state.cart.each do |product_id, quantity| + product = Product.find_by_id( product_id ) + subtotal = ( product.price * quantity.to_f ).round(6) + line = { "quantity" => quantity, + "title" => product.title, + "price" => product.price, + "subtotal" => subtotal, + "id" => product_id } + @products << line + @sum = ( @sum + subtotal ).round(6) + end + render :card + else + redirect Index + end + end + + def post + if @input.has_key?( "cleanup" ) + if @input.cleanup == "all" + @state.cart = nil + redirect Index + elsif @input.cleanup =~ /\A\d+\Z/ + @state.cart = @state.cart.select{ |x| x != @input.cleanup.to_i } + redirect Cart + end + end + end + end + + class Checkout < R '/checkout' + def get + unless @state.cart.blank? + render :checkout + else + redirect Index + end + end + + def post + order = Order.create( :fullname => @input.fullname, + :addrline1 => @input.addrline1, + :zippostalcode => @input.zippostalcode, + :city => @input.city, + :country => @input.country, + :cart => @state.cart.to_s, + :bitcoinaddress => "15zveKrrJWbp3BVpv36VWr1kPq3opNK6xf", + :addrline2 => @input.addrline2, + :stateprovinceregion => @input.stateprovinceregion, + :email => @input.email, + :orderdate => Time.now.strftime( "%Y-%m-%dT%H:%M:%S" ) ) + if order.errors.any? + @error = 'ERROR: Creation of order failed! Please check the form and try again.' + render :checkout, @input + else + @state.order = "done" + # We skip autocreation of bitcoin addresses here! + redirect Bitcoin + end + end + end + + class Bitcoin < R '/bitcoin' + def get + unless @state.cart.blank? + if @state.has_key?( "order" ) + if @state.order == "done" + @state.order = nil + @state.cart = nil + render :bitcoin + end + else + redirect Checkout + end + else + redirect Index + end + end + end +end diff --git a/app/models.rb b/app/models.rb new file mode 100755 index 0000000..cfdf35c --- /dev/null +++ b/app/models.rb @@ -0,0 +1,16 @@ +#!/usr/bin/env ruby + +module Drugstore::Models + class Product < Base + after_find do + ActiveRecord::Base.clear_active_connections! + end + end + + class Order < Base + validates :fullname, :addrline1, :zippostalcode, :city, :country, :cart, :bitcoinaddress, presence: true + after_create do + ActiveRecord::Base.clear_active_connections! + end + end +end \ No newline at end of file diff --git a/app/root.rb b/app/root.rb new file mode 100755 index 0000000..ff57b21 --- /dev/null +++ b/app/root.rb @@ -0,0 +1,26 @@ +#!/usr/bin/env ruby + +HeadTitle = "Drugstore" +HeaderH1 = "Hidden drugstore" + +module Drugstore + include Camping::Session + set :secret, '48be8eda8ea970a3f621974822091127895784c0' + def service(*) + @headers['Content-Type'] = 'text/html; charset=utf-8' + super + end + + def r404( path ) + "Sorry, but Magento Community Edition can't find #{path}." + end + + def r501( method ) + "Sorry, but Magento Community Edition can't respond to #{method}." + end + + def r500( klass, method, ex ) + "Sorry, but #{klass}##{method} failed with #{ex}." + end + +end \ No newline at end of file diff --git a/app/views.rb b/app/views.rb new file mode 100755 index 0000000..0c3d6e8 --- /dev/null +++ b/app/views.rb @@ -0,0 +1,205 @@ +#!/usr/bin/env ruby + +module Drugstore::Views + # Layout for all sites + def layout + html do + head do + title HeadTitle + style do + text 'h1{background-color:grey;color:white;padding-left:8px;margin-left:-8px;margin-right:-8px;}' + text '.greenbutton{background-color:green;font-weight:bold;color:white;}' + text 'table,th,td{border:1px dotted;border-collapse:collapse;padding:8px;}' + text 'th,td{text-align:center;}' + text '.redbutton{background-color:red;font-weight:bold;color:white;}' + text '.boldgreen{font-weight:bold;color:green;}' + text '.boldred{font-weight:bold;color:red;}' + text 'fieldset{width:32em;}' + text 'fieldset label{display:inline;float:left;width:12em;}' + end + end + body do + center 'This is NOT a real drugstore. This site just exists for educational purpose!', :class => "boldred" + h1 HeaderH1 + self << yield + hr + p do + text '(c) 2016 - ' + a 'The Onion Root', :href => 'mailto:the-onion-root@riseup.net' + end + center 'This is NOT a real drugstore. This site just exists for educational purpose!', :class => "boldred" + end + end + end + + # Index and Catalog + def index + if @products.empty? + h2 'No products found' + p do + text 'Could not find any products.' + br + text 'Please try again later.' + end + else + h2 'Products' + if @cart == 0 + p { text 'Items on cart: ' + @cart.to_s } + else + p do + a 'Items on cart: ' + @cart.to_s, :href => "/cart" + if @state.info + span @state.info, :class => "boldgreen" + @state.info = nil + end + end + end + @products.each do |product| + hr + _product( product ) + end + end + end + + # Cart + def card + h2 'Cart' + p { a '<- Back to the calalog' + @cart.to_s, :href => "/" } + hr + unless @products.empty? + form :action => "/cart", :method => 'post' do + input :name => 'cleanup', :type => 'hidden', :value => "all" + input :type => 'submit', :value => 'Delete Cart', :class => "redbutton" + end + br + table do + tr do + th 'Quantity (Gramm)' + th 'Title' + th 'Price (Bitcoin)' + th 'Subtotal (Bitcoin)' + th 'Action' + end + @products.each do |product| + tr do + td product[ "quantity" ] + td product[ "title" ] + td product[ "price" ] + td do + b product[ "subtotal" ] + end + td do + form :action => "/cart", :method => 'post' do + input :name => 'cleanup', :type => 'hidden', :value => product[ "id" ] + input :type => 'submit', :value => 'Delete product', :class => "redbutton" + end + end + end + end + end + p do + text 'Total: ' + span @sum.to_s, :class => "boldred" + text ' BTC' + end + form :action => "/checkout" do + input :type => "submit", :value => "Checkout", :class => "greenbutton" + end + p 'Hint: the max of gramms per product per order is set at 10.' + else + p 'Cart is empty.' + end + end + + def checkout + h2 'Checkout' + p { a '<- Back to the cart', :href => "/cart" } + hr + if @error + p @error, :class => "boldred" + end + form :action => "/checkout", :method => 'post', :id => "shippingaddress" do + label 'Shipping Address:', :form => "shippingaddress" + fieldset do + label 'Full Name:', :for => 'fullname', :class => 'boldred' + input :name => 'fullname', :type => 'text', :size => "30", :value => @input.fullname + br + label 'Address Line 1:', :for => 'addrline1', :class => 'boldred' + input :name => 'addrline1', :type => 'text', :size => "30", :value => @input.addrline1 + br + label 'Address Line 2:', :for => 'addrline2' + input :name => 'addrline2', :type => 'text', :size => "30", :value => @input.addrline2 + br + label 'City:', :for => 'city', :class => 'boldred' + input :name => 'city', :type => 'text', :size => "30", :value => @input.city + br + label 'State/Province/Region:', :for => 'stateprovinceregion' + input :name => 'stateprovinceregion', :type => 'text', :size => "30", :value => @input.stateprovinceregion + br + label 'ZIP/Postal Code:', :for => 'zippostalcode', :class => 'boldred' + input :name => 'zippostalcode', :type => 'text', :size => "30", :value => @input.zippostalcode + br + label 'Country:', :for => 'country', :class => 'boldred' + input :name => 'country', :type => 'text', :size => "30", :value => @input.country + br + label 'Email:', :for => 'email' + input :name => 'email', :type => 'text', :size => "30", :value => @input.email + br + end + br + input :type => 'submit', :value => 'Place order', :class => "greenbutton" + end + p do + text 'Some hints:' + ul do + li { span 'Fields with a bold red label are mandatory fields!', :class => "boldred" } + li 'After successfully placing the order you will get a bitcoin address.' + li 'Shipping will be done after you have proceeded a transaction to that address.' + li 'Unpayed orders will be deleted in the database after the request expires (1 day).' + end + end + end + + def bitcoin + h2 'Bitcoin' + p { a '<- Back to the calalog' + @cart.to_s, :href => "/" } + hr + br + br + br + center do + p '15zveKrrJWbp3BVpv36VWr1kPq3opNK6xf' + img :src => "img/15zveKrrJWbp3BVpv36VWr1kPq3opNK6xf.png", :alt => 'QR code' + end + br + br + br + p do + text 'Hint: ' + span "This bitcoin address has expired. DON'T use it!", :class => "boldred" + end + end + + # Partials + def _product( product ) + img :src => product.image_url, :alt => product.title + h3 product.title + p do + b 'Description' + text ': ' + product.description + br + br + b 'Price per gramm' + text ': ' + product.price.to_s + ' Bitcoin(s)' + end + form :action => "/", :method => 'post' do + input :name => 'product_id', :type => 'hidden', :value => product.id + tag! :select, :name => 'quantity' do + (1..10).each do |q| + tag! :option, q + end + end + input :type => 'submit', :value => 'Add to card', :class => "greenbutton" + end + end +end diff --git a/cnf/db.yml b/cnf/db.yml new file mode 100644 index 0000000..c1ec86b --- /dev/null +++ b/cnf/db.yml @@ -0,0 +1,6 @@ +--- +production: + adapter: sqlite3 + database: /home/dealer/drugstore/db/drugstore.sqlite + pool: 15 + timeout: 5000 diff --git a/cnf/thin.yml b/cnf/thin.yml new file mode 100644 index 0000000..6c59fbd --- /dev/null +++ b/cnf/thin.yml @@ -0,0 +1,21 @@ +--- +chdir: /home/dealer/drugstore +environment: production +address: 127.0.0.1 +port: 3000 +timeout: 30 +log: log/thin.log +pid: tmp/pids/thin.pid +max_conns: 1024 +max_persistent_conns: 512 +require: [] +wait: 30 +threadpool_size: 20 +servers: 3 +rackup: config.ru +daemonize: true +user: neupat75 +group: staff +threaded: true +no-epoll: true # linux only! +tag: Drugstore diff --git a/config.ru b/config.ru new file mode 100755 index 0000000..bee1b1d --- /dev/null +++ b/config.ru @@ -0,0 +1,17 @@ +#!/usr/bin/env ruby + +require 'camping' +require 'camping/ar' +require 'camping/session' + +db = YAML.load( File.read( 'cnf/db.yml' ) ) +ActiveRecord::Base.establish_connection( db['production'] ) + +Camping.goes :Drugstore + +require_relative 'app/root' +require_relative 'app/models' +require_relative 'app/views' +require_relative 'app/controllers' + +run Drugstore diff --git a/db/drugstore.sqlite b/db/drugstore.sqlite new file mode 100644 index 0000000..41a19ce Binary files /dev/null and b/db/drugstore.sqlite differ diff --git a/drugstore b/drugstore new file mode 100755 index 0000000..be3ed42 --- /dev/null +++ b/drugstore @@ -0,0 +1,21 @@ +#!/bin/sh + +usage() { + printf "\nUsage: ${0} start|restart|stop\n\n" +} + +if [ "${#}" -ne 1 ] ; then + printf "\nError: less or more then one command are given.\n" + usage + exit +fi + +if ! [[ "${1}" =~ start|restart|stop ]] ; then + printf "\nError: only start, restart or stop are allowed.\n" + usage + exit +fi + +thin "${1}" -C cnf/thin.yml + +exit 0 diff --git a/lighttpd.conf.sample.conf b/lighttpd.conf.sample.conf new file mode 100644 index 0000000..543b1e5 --- /dev/null +++ b/lighttpd.conf.sample.conf @@ -0,0 +1,27 @@ +# Debian GNU/Linux "Jessie" defaults: +server.modules = ( "mod_access", "mod_compress", "mod_proxy" ) +server.upload-dirs = ( "/var/cache/lighttpd/uploads" ) +server.errorlog = "/var/log/lighttpd/error.log" +server.pid-file = "/var/run/lighttpd.pid" +server.username = "www-data" +server.groupname = "www-data" +server.port = 80 +url.access-deny = ( "~" ) +compress.cache-dir = "/var/cache/lighttpd/compress/" +include_shell "/usr/share/lighttpd/create-mime.assign.pl" +include_shell "/usr/share/lighttpd/include-conf-enabled.pl" + +# Hidden Drugstore modifications/additions: +#index-file.names = ( "index.html", "index.lighttpd.html" ) +compress.filetype = ( "text/plain", "text/html" ) +server.document-root = "/home/dealer/drugstore/pub" +server.bind = "127.0.0.1" +server.tag = "Microsoft-IIS/8.0" +proxy.balance = "fair" +$HTTP["url"] !~ "\.(txt|html|ico|jpg|png)$" { + proxy.server = ( "" => ( + ( "host" => "127.0.0.1", "port" => 3000 ), + ( "host" => "127.0.0.1", "port" => 3001 ), + ( "host" => "127.0.0.1", "port" => 3002 ), + ) ) +} \ No newline at end of file diff --git a/log/thin.3000.log b/log/thin.3000.log new file mode 100644 index 0000000..e69de29 diff --git a/log/thin.3001.log b/log/thin.3001.log new file mode 100644 index 0000000..e69de29 diff --git a/log/thin.3002.log b/log/thin.3002.log new file mode 100644 index 0000000..e69de29 diff --git a/pub/favicon.ico b/pub/favicon.ico new file mode 100644 index 0000000..48060b1 Binary files /dev/null and b/pub/favicon.ico differ diff --git a/pub/img/15zveKrrJWbp3BVpv36VWr1kPq3opNK6xf.png b/pub/img/15zveKrrJWbp3BVpv36VWr1kPq3opNK6xf.png new file mode 100755 index 0000000..08f9d9a Binary files /dev/null and b/pub/img/15zveKrrJWbp3BVpv36VWr1kPq3opNK6xf.png differ diff --git a/pub/img/broken_cookies.jpg b/pub/img/broken_cookies.jpg new file mode 100644 index 0000000..6709765 Binary files /dev/null and b/pub/img/broken_cookies.jpg differ diff --git a/pub/img/dark_chocolate.jpg b/pub/img/dark_chocolate.jpg new file mode 100644 index 0000000..75fc5bd Binary files /dev/null and b/pub/img/dark_chocolate.jpg differ diff --git a/pub/img/salt_crystal.jpg b/pub/img/salt_crystal.jpg new file mode 100644 index 0000000..8343b26 Binary files /dev/null and b/pub/img/salt_crystal.jpg differ diff --git a/pub/robots.txt b/pub/robots.txt new file mode 100644 index 0000000..c6742d8 --- /dev/null +++ b/pub/robots.txt @@ -0,0 +1,2 @@ +User-Agent: * +Disallow: / diff --git a/tmp/pids/.placeholder b/tmp/pids/.placeholder new file mode 100644 index 0000000..e69de29