├── .gitignore ├── Gemfile ├── Gemfile.lock ├── README.md ├── Rakefile ├── app ├── assets │ ├── images │ │ └── .keep │ ├── javascripts │ │ ├── application.js │ │ ├── orders.js.coffee │ │ ├── products.js.coffee │ │ └── store.js.coffee │ └── stylesheets │ │ ├── application.css.scss │ │ └── products.css.scss ├── controllers │ ├── application_controller.rb │ ├── concerns │ │ └── .keep │ ├── orders_controller.rb │ └── products_controller.rb ├── helpers │ ├── application_helper.rb │ ├── orders_helper.rb │ └── products_helper.rb ├── mailers │ └── .keep ├── models │ ├── .keep │ ├── amount.rb │ ├── asset.rb │ ├── concerns │ │ └── .keep │ ├── order.rb │ ├── photo.rb │ ├── product.rb │ ├── shipping.rb │ ├── subscription.rb │ ├── tx.rb │ └── wallet.rb └── views │ ├── layouts │ └── application.html.erb │ ├── orders │ ├── show.html.erb │ └── stale.html.erb │ ├── products │ ├── _form.html.erb │ ├── edit.html.erb │ ├── index.html.erb │ ├── new.html.erb │ ├── report.html.erb │ └── show.html.erb │ └── shared │ ├── _asset_select.html.erb │ ├── _flash.html.erb │ ├── _ga.html.erb │ ├── _header.html.erb │ └── _network_alerts.html.erb ├── bin ├── bundle ├── rails ├── rake └── spring ├── config.ru ├── config ├── application.rb ├── boot.rb ├── database.yml.example ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── assets.rb │ ├── backtrace_silencers.rb │ ├── bitshares.rb │ ├── cookies_serializer.rb │ ├── filter_parameter_logging.rb │ ├── inflections.rb │ ├── mime_types.rb │ ├── session_store.rb │ └── wrap_parameters.rb ├── locales │ └── en.yml ├── routes.rb ├── secrets.yml.example ├── store.yml.example └── unicorn.rb.example ├── db ├── migrate │ ├── 20141105025954_create_txes.rb │ ├── 20141211061531_create_shippings.rb │ ├── 20141211061539_create_products.rb │ ├── 20141211072149_create_orders.rb │ ├── 20141230210006_create_subscriptions.rb │ ├── 20141231091412_extras.rb │ ├── 20150101202813_final_extras.rb │ ├── 20150113091706_add_currencies.rb │ └── 20150119184918_create_photos.rb ├── schema.rb └── seeds.rb ├── lib ├── assets │ └── .keep ├── graphene_api.rb └── tasks │ └── .keep ├── public ├── 404.html ├── 422.html ├── 500.html ├── bts-10x16.png ├── bts-15x24.png ├── bts-20x32.png ├── bts-22x24.png ├── bts-30x48.png ├── bts-white-sm.png ├── bts-white.png ├── cashier.jpg ├── favicon.ico ├── launch3.jpg ├── mystery.jpg └── robots.txt └── vendor └── assets ├── javascripts └── .keep └── stylesheets └── .keep /.gitignore: -------------------------------------------------------------------------------- 1 | *.rbc 2 | capybara-*.html 3 | .rspec 4 | /log 5 | /tmp 6 | /db/*.sqlite3 7 | /public/assets 8 | /public/system 9 | /coverage/ 10 | /spec/tmp 11 | **.orig 12 | rerun.txt 13 | pickle-email-*.html 14 | 15 | config/initializers/secret_token.rb 16 | config/secrets.yml 17 | 18 | config/database.yml 19 | config/store.yml 20 | config/unicorn.rb 21 | 22 | ## Environment normalisation: 23 | /.bundle 24 | /vendor/bundle 25 | 26 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 27 | .rvmrc 28 | 29 | test 30 | *.sh 31 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | 4 | # Cryptofresh 5 | 6 | gem 'rails', '4.2.4' 7 | gem 'web-console', '~> 2.0' 8 | gem 'mysql2', '~> 0.3.18' 9 | gem 'paperclip' 10 | gem 'bootstrap-sass' 11 | gem 'unicorn' 12 | 13 | 14 | # Defaults 15 | 16 | gem 'sass-rails', '~> 4.0.3' 17 | gem 'uglifier', '>= 1.3.0' 18 | gem 'coffee-rails', '~> 4.0.0' 19 | gem 'jquery-rails' 20 | gem 'turbolinks' 21 | gem 'spring', group: :development 22 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | actionmailer (4.2.4) 5 | actionpack (= 4.2.4) 6 | actionview (= 4.2.4) 7 | activejob (= 4.2.4) 8 | mail (~> 2.5, >= 2.5.4) 9 | rails-dom-testing (~> 1.0, >= 1.0.5) 10 | actionpack (4.2.4) 11 | actionview (= 4.2.4) 12 | activesupport (= 4.2.4) 13 | rack (~> 1.6) 14 | rack-test (~> 0.6.2) 15 | rails-dom-testing (~> 1.0, >= 1.0.5) 16 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 17 | actionview (4.2.4) 18 | activesupport (= 4.2.4) 19 | builder (~> 3.1) 20 | erubis (~> 2.7.0) 21 | rails-dom-testing (~> 1.0, >= 1.0.5) 22 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 23 | activejob (4.2.4) 24 | activesupport (= 4.2.4) 25 | globalid (>= 0.3.0) 26 | activemodel (4.2.4) 27 | activesupport (= 4.2.4) 28 | builder (~> 3.1) 29 | activerecord (4.2.4) 30 | activemodel (= 4.2.4) 31 | activesupport (= 4.2.4) 32 | arel (~> 6.0) 33 | activesupport (4.2.4) 34 | i18n (~> 0.7) 35 | json (~> 1.7, >= 1.7.7) 36 | minitest (~> 5.1) 37 | thread_safe (~> 0.3, >= 0.3.4) 38 | tzinfo (~> 1.1) 39 | arel (6.0.3) 40 | autoprefixer-rails (6.0.3) 41 | execjs 42 | json 43 | binding_of_caller (0.7.2) 44 | debug_inspector (>= 0.0.1) 45 | bootstrap-sass (3.3.5) 46 | autoprefixer-rails (>= 5.0.0.1) 47 | sass (>= 3.2.19) 48 | builder (3.2.2) 49 | climate_control (0.0.3) 50 | activesupport (>= 3.0) 51 | cocaine (0.5.7) 52 | climate_control (>= 0.0.3, < 1.0) 53 | coffee-rails (4.0.1) 54 | coffee-script (>= 2.2.0) 55 | railties (>= 4.0.0, < 5.0) 56 | coffee-script (2.4.1) 57 | coffee-script-source 58 | execjs 59 | coffee-script-source (1.9.1.1) 60 | debug_inspector (0.0.2) 61 | erubis (2.7.0) 62 | execjs (2.6.0) 63 | globalid (0.3.6) 64 | activesupport (>= 4.1.0) 65 | hike (1.2.3) 66 | i18n (0.7.0) 67 | jquery-rails (4.0.5) 68 | rails-dom-testing (~> 1.0) 69 | railties (>= 4.2.0) 70 | thor (>= 0.14, < 2.0) 71 | json (1.8.3) 72 | kgio (2.10.0) 73 | loofah (2.0.3) 74 | nokogiri (>= 1.5.9) 75 | mail (2.6.3) 76 | mime-types (>= 1.16, < 3) 77 | mime-types (2.6.2) 78 | mimemagic (0.3.0) 79 | mini_portile (0.6.2) 80 | minitest (5.8.1) 81 | multi_json (1.11.2) 82 | mysql2 (0.3.18) 83 | nokogiri (1.6.6.2) 84 | mini_portile (~> 0.6.0) 85 | paperclip (4.3.1) 86 | activemodel (>= 3.2.0) 87 | activesupport (>= 3.2.0) 88 | cocaine (~> 0.5.5) 89 | mime-types 90 | mimemagic (= 0.3.0) 91 | rack (1.6.4) 92 | rack-test (0.6.3) 93 | rack (>= 1.0) 94 | rails (4.2.4) 95 | actionmailer (= 4.2.4) 96 | actionpack (= 4.2.4) 97 | actionview (= 4.2.4) 98 | activejob (= 4.2.4) 99 | activemodel (= 4.2.4) 100 | activerecord (= 4.2.4) 101 | activesupport (= 4.2.4) 102 | bundler (>= 1.3.0, < 2.0) 103 | railties (= 4.2.4) 104 | sprockets-rails 105 | rails-deprecated_sanitizer (1.0.3) 106 | activesupport (>= 4.2.0.alpha) 107 | rails-dom-testing (1.0.7) 108 | activesupport (>= 4.2.0.beta, < 5.0) 109 | nokogiri (~> 1.6.0) 110 | rails-deprecated_sanitizer (>= 1.0.1) 111 | rails-html-sanitizer (1.0.2) 112 | loofah (~> 2.0) 113 | railties (4.2.4) 114 | actionpack (= 4.2.4) 115 | activesupport (= 4.2.4) 116 | rake (>= 0.8.7) 117 | thor (>= 0.18.1, < 2.0) 118 | raindrops (0.15.0) 119 | rake (10.4.2) 120 | sass (3.2.19) 121 | sass-rails (4.0.5) 122 | railties (>= 4.0.0, < 5.0) 123 | sass (~> 3.2.2) 124 | sprockets (~> 2.8, < 3.0) 125 | sprockets-rails (~> 2.0) 126 | spring (1.4.0) 127 | sprockets (2.12.4) 128 | hike (~> 1.2) 129 | multi_json (~> 1.0) 130 | rack (~> 1.0) 131 | tilt (~> 1.1, != 1.3.0) 132 | sprockets-rails (2.3.3) 133 | actionpack (>= 3.0) 134 | activesupport (>= 3.0) 135 | sprockets (>= 2.8, < 4.0) 136 | thor (0.19.1) 137 | thread_safe (0.3.5) 138 | tilt (1.4.1) 139 | turbolinks (2.5.3) 140 | coffee-rails 141 | tzinfo (1.2.2) 142 | thread_safe (~> 0.1) 143 | uglifier (2.7.2) 144 | execjs (>= 0.3.0) 145 | json (>= 1.8.0) 146 | unicorn (4.9.0) 147 | kgio (~> 2.6) 148 | rack 149 | raindrops (~> 0.7) 150 | web-console (2.2.1) 151 | activemodel (>= 4.0) 152 | binding_of_caller (>= 0.7.2) 153 | railties (>= 4.0) 154 | sprockets-rails (>= 2.0, < 4.0) 155 | 156 | PLATFORMS 157 | ruby 158 | 159 | DEPENDENCIES 160 | bootstrap-sass 161 | coffee-rails (~> 4.0.0) 162 | jquery-rails 163 | mysql2 (~> 0.3.18) 164 | paperclip 165 | rails (= 4.2.4) 166 | sass-rails (~> 4.0.3) 167 | spring 168 | turbolinks 169 | uglifier (>= 1.3.0) 170 | unicorn 171 | web-console (~> 2.0) 172 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cryptofresh 2 | Webstore accepting BitAsset payments. 3 | 4 | ### Refactoring in Progress! 5 | 6 | ### Getting Started 7 | 8 | - config/database.yml.example 9 | - config/unicorn.yml.example 10 | - config/store.yml.example 11 | 12 | cashier_acct: cashier 13 | rpc_port: 9999 14 | rpc_user: user 15 | rpc_pass: pass 16 | admin_user: # login for store manager 17 | admin_pass: # pass for store manager 18 | secret_phrase: # random pass, secures downloads 19 | 20 | ### Todo 21 | 22 | - Check wallet is responding and blockchain is current. Disable the store otherwise. 23 | - Change floats to BigDecimal 24 | - Categories (enable STI on products table) 25 | - "Refund" button 26 | - Accept partial payments? 27 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path('../config/application', __FILE__) 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roadscape/cryptofresh/af471800ca5a2e21c5a311fd38839e49f0f79774/app/assets/images/.keep -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. 9 | // 10 | // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require jquery 14 | 15 | //= require jquery 16 | //= require bootstrap-sprockets 17 | 18 | //= require jquery_ujs 19 | //= require_tree . 20 | -------------------------------------------------------------------------------- /app/assets/javascripts/orders.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/products.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/store.js.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.css.scss: -------------------------------------------------------------------------------- 1 | @import "bootstrap-sprockets"; 2 | @import 'bootstrap'; 3 | 4 | header {padding: 15px 0 10px;} 5 | header .text-muted {font-style: italic; font-size: 15px; color: #AAA;} 6 | footer {padding: 0 2em 1.5em; color: #AAA; text-align: left;} 7 | #delegate {text-align: right; jfloat: right;} 8 | #delegate a {float: ight; font-size: 12px; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; opacity: 0.9;} 9 | #delegate a:hover {opacity: 1;} 10 | #launch {border-radius: 10px; box-shadow: inset 0 0 20px rgba(0,0,0,0.25); margin-bottom: 20px; position: relative; margin: 1% 0 2%;} 11 | #launch {background: #5e7598 url(/launch3.jpg) 0 0; padding: 5% 0 4%;} 12 | #launch.mini {padding: 0.2% 0; margin: 2% 0 3%;} 13 | #launch.mini #logo {line-height: 80%;} 14 | #launch.mini #fl {opacity: 0.5;} 15 | #launch.mini #tag {xbackground-color: rgba(249,242,244,0.5); opacity: 0.8;} 16 | #launch #fl {position: absolute; width: 100%; height: 100%; top: 0; left: 0; background: url(/bts-white-sm.png) 98% 93% no-repeat;} 17 | #logo, #tag {display: inline-block; vertical-align: middle;} 18 | #logo {font-size: 57px; margin: 0.25em 0 0.25em 0.55em; border-right: 4px solid white; padding-right: 0.4em; color: rgba(255,255,255,0.9);} 19 | #tag {background-color: rgba(249,242,244,0.7); color: #080; font-size: 23px; padding-left: 10px; margin-left: 1.3em;} 20 | #tag .inv {color: #ccc; background: #383;} 21 | 22 | .asset {padding: 3px; color: white !important; display: inline-block; font-size: 14px; border-radius: 3px; margin-right: 12px; letter-spacing: 0.05em; min-width: 100px;} 23 | .asset .ico {display: inline-block; width: 22px; height: 24px; background: url(/bts-22x24.png); vertical-align: middle; margin-right: 6px; border-radius: 2px;} 24 | .asset .lbl {display: inline-block; vertical-align: middle;} 25 | a.asset {opacity: 0.43; border: 1px solid #888; text-decoration: none;} 26 | a.asset:hover, 27 | span.asset {opacity: 1; box-shadow: 0px 0px 6px 0px rgba(0,0,0,0.75); border: 1px solid #555;} 28 | 29 | .bts-bg-usd {background-color: #9bc63f;} 30 | .bts-bg-cny {background-color: #d6595e;} 31 | .bts-bg-gold {background-color: #d2bd43;} 32 | .bts-bg-silver {background-color: #a1b4b7;} 33 | .bts-bg-eur {background-color: #398abe;} 34 | .bts-bg-bts {background-color: #3ba4db;} 35 | 36 | .bts-text-usd {color: #9bc63f;} 37 | .bts-text-cny {color: #d6595e;} 38 | .bts-text-gold {color: #d2bd43;} 39 | .bts-text-silver {color: #a1b4b7;} 40 | .bts-text-eur {color: #398abe;} 41 | .bts-text-bts {color: #3ba4db;} 42 | -------------------------------------------------------------------------------- /app/assets/stylesheets/products.css.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Products controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | # Prevent CSRF attacks by raising an exception. 3 | # For APIs, you may want to use :null_session instead. 4 | protect_from_forgery with: :exception 5 | 6 | before_filter :check_asset 7 | before_filter :check_ref 8 | 9 | def check_asset 10 | asset = params[:asset] 11 | session[:asset] = asset if asset && Asset.valid?(asset) 12 | session[:asset] ||= 'USD' 13 | end 14 | 15 | def check_ref 16 | if params[:r] 17 | if cookies[:referer].blank? 18 | cookies[:referer] ||= {:value => params[:r], :expires => 2.weeks.from_now} 19 | end 20 | end 21 | end 22 | 23 | end 24 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roadscape/cryptofresh/af471800ca5a2e21c5a311fd38839e49f0f79774/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/controllers/orders_controller.rb: -------------------------------------------------------------------------------- 1 | class OrdersController < ApplicationController 2 | skip_before_action :verify_authenticity_token 3 | before_filter :load_order 4 | 5 | def show 6 | render 'stale' if @order.stale? 7 | end 8 | 9 | def status 10 | Wallet.scan 11 | 12 | render :js => case 13 | when @order.paid? && @order.is_dd? 14 | "notify_dl('#{download_order_url(@order)}');" 15 | when @order.paid? 16 | "notify_ship();" 17 | else 18 | "" 19 | end 20 | end 21 | 22 | def download 23 | raise "Order is stale" if @order.stale? 24 | raise "Order is not paid" unless @order.paid? 25 | raise "Not downloadable" unless @order.is_dd? 26 | 27 | p = @order.product 28 | send_file p.dl.path, 29 | :filename => p.dl_file_name, 30 | :type => p.dl_content_type, 31 | :disposition => 'attachment' 32 | end 33 | 34 | private 35 | def load_order 36 | @order = Order.from_param(params[:id]) 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /app/controllers/products_controller.rb: -------------------------------------------------------------------------------- 1 | class ProductsController < ApplicationController 2 | http_basic_authenticate_with name: STORE['admin_user'], password: STORE['admin_pass'], except: [:subscribe, :index, :show, :order] 3 | 4 | def subscribe 5 | sub = Subscription.new(subscription_params) 6 | redirect_to root_url, notice: (sub.save ? "Subscribed!" : "Invalid entry.") 7 | end 8 | 9 | def index 10 | @products = Product.categories 11 | end 12 | 13 | def show 14 | @product = Product.find(params['id']) 15 | if @product.children.size == 1 16 | redirect_to @product.children.first 17 | else 18 | @order = @product.new_order 19 | end 20 | end 21 | 22 | def order 23 | @order = Order.new(order_params.merge( 24 | :ip => request.remote_ip, 25 | :referrer_acct => cookies[:referer], 26 | :bts_asset_id => session[:asset])) 27 | 28 | if @order.save 29 | redirect_to @order 30 | else 31 | @product = @order.product 32 | render 'show' 33 | end 34 | end 35 | 36 | 37 | def report 38 | end 39 | 40 | def new 41 | @product = Product.new(:parent_id => params[:id]) 42 | end 43 | 44 | def create 45 | @product = Product.new(product_params) 46 | if @product.save 47 | redirect_to @product, notice: "All good!" 48 | else 49 | render 'new' 50 | end 51 | end 52 | 53 | def edit 54 | @product = Product.find(params['id']) 55 | end 56 | 57 | def update 58 | @product = Product.find(params['id']) 59 | if @product.update_attributes(product_params) 60 | redirect_to edit_product_url, notice: "All good!" 61 | else 62 | render 'edit' 63 | end 64 | end 65 | 66 | private 67 | 68 | def subscription_params 69 | params.require(:subscription).permit(:email) 70 | end 71 | 72 | def order_params 73 | params.require('order').permit(:product_id, :shipping_id, :address, :email) 74 | end 75 | 76 | def product_params 77 | params.require('product').permit(:name, :desc, :parent_id, 78 | :cents, :position, :default_id, :dl, :image, :is_category, :code, :short_desc, 79 | :button_label, :num_stock, :num_sold, :node_type, :royalty_acct, :royalty_cents, :refer_cents, 80 | :photos_attributes => [:image, :_destroy, :id]) 81 | end 82 | end 83 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | 3 | def local_price obj, show_full = false 4 | amt = obj.amount.convert(session[:asset]) 5 | out = show_full ? amt.full : amt.short 6 | out.sub!(/.00(?=[^\d])/, '') 7 | out = out.sub("oz", "oz").html_safe if show_full 8 | out 9 | end 10 | 11 | def bootstrap_class_for(flash_type) 12 | { :success => 'alert-success', 13 | :error => 'alert-danger', 14 | :alert => 'alert-warning', 15 | :notice => 'alert-info' 16 | }[flash_type.to_sym] 17 | end 18 | 19 | end 20 | -------------------------------------------------------------------------------- /app/helpers/orders_helper.rb: -------------------------------------------------------------------------------- 1 | module OrdersHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/products_helper.rb: -------------------------------------------------------------------------------- 1 | module ProductsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roadscape/cryptofresh/af471800ca5a2e21c5a311fd38839e49f0f79774/app/mailers/.keep -------------------------------------------------------------------------------- /app/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roadscape/cryptofresh/af471800ca5a2e21c5a311fd38839e49f0f79774/app/models/.keep -------------------------------------------------------------------------------- /app/models/amount.rb: -------------------------------------------------------------------------------- 1 | class Amount 2 | 3 | attr_reader :amount, :symbol, :prefix 4 | 5 | NICE_DECIMALS = {usd: 2, eur: 2, cny: 2, silver: 2, gold: 4, btc: 4, bts: 0} 6 | 7 | def self.asset id 8 | @asset = {} unless @asset 9 | @asset[id] ||= (legacy_asset(id) || Graphene::API::RPC.instance.get_asset(id)) 10 | end 11 | 12 | def self.cent c 13 | Amount.new(c / 100.0, 'USD') 14 | end 15 | 16 | def self.from_bts arr 17 | a = asset(arr['asset_id']) 18 | Amount.new(arr['amount'].to_f / 10**a['precision'], a['symbol']) 19 | end 20 | 21 | # Amount.new(1.99, "USD") 22 | def initialize human_amount, symbol 23 | @amount = human_amount 24 | @symbol = symbol 25 | @prefix = (symbol == 'BTS' ? '' : 'Bit') 26 | end 27 | 28 | # Convert nicely e.g. 1.99 USD --> 12.99 CNY 29 | def convert to_symbol 30 | return self if to_symbol == @symbol 31 | to_amount = @amount * Asset.rate(@symbol, to_symbol) 32 | nicely_round Amount.new(to_amount, to_symbol) 33 | end 34 | 35 | # Add another amount e.g. 0.49 + 0.49 --> 0.98 36 | def add new_amount 37 | raise "Symbol mismatch" unless @symbol == new_amount.symbol 38 | dec = NICE_DECIMALS[@symbol.downcase.to_sym] 39 | Amount.new(BigDecimal.new(@amount, dec) + BigDecimal.new(new_amount.amount, dec), @symbol) 40 | end 41 | 42 | # Does this amount COVER the other? 43 | def includes? amt 44 | return nil if symbol != amt.symbol 45 | @amount >= amt.amount 46 | end 47 | 48 | 49 | 50 | def short 51 | "#{unit} #{@symbol}" 52 | end 53 | 54 | def full 55 | "#{unit} #{@prefix}#{@symbol}" 56 | end 57 | 58 | def unit 59 | s = @amount.to_s.sub(/\.0+$/, '') 60 | case @symbol 61 | when 'USD' then "$%s" 62 | when 'CNY' then "¥%s" 63 | when 'JPY' then "¥%s" 64 | when 'EUR' then "€%s" 65 | when 'GOLD' then "%soz" 66 | when 'SILVER' then "%soz" 67 | else "%s" 68 | end % s 69 | end 70 | 71 | def bts_asset_id 72 | Amount.asset(@symbol)['symbol'] 73 | end 74 | 75 | def bts_amount 76 | (@amount.to_f * 10**Amount.asset(@symbol)['precision']).to_i 77 | end 78 | 79 | 80 | 81 | 82 | private 83 | 84 | def nicely_round amount 85 | dec = NICE_DECIMALS[amount.symbol.downcase.to_sym] 86 | raise("Don't know precision for #{asset}") unless dec 87 | 88 | # Truncate the amount, e.g. $1.001 -> $1.01 89 | num = BigDecimal.new(amount.amount.to_s).ceil(dec) 90 | 91 | # Round the number to certain thresholds 92 | nice_num = case 93 | when num == 0 then "0" 94 | when num < 0.005 then ("%0.04f" % num).gsub(/0+$/, '') 95 | when num < 0.05 then ("%0.03f" % num).gsub(/0+$/, '') 96 | when num < 0.25 then "%0.02f" % num 97 | when num < 0.4 then "%0.02f" % round(num, 0.05, 0.01) 98 | when num < 2 then "%0.02f" % round(num, 0.10, 0.01) 99 | when num <20&&dec>1 then "%0.02f" % round(num, 0.50, 0.01) 100 | when num < 20 then "%d" % round(num, 1, 0) 101 | when num < 75 then "%d" % round(num, 5, 1) 102 | when num < 500 then "%d" % round(num, 10, 1) 103 | when num < 5000 then "%d" % round(num, 100, 1) 104 | when num < 50000 then "%d" % round(num, 1000, 1) 105 | when num < 500000 then "%d" % round(num, 10000, 1) 106 | when num < 5000000 then "%d" % round(num, 100000, 1) 107 | else "%d" % round(num, 1000000, 1) 108 | end 109 | 110 | Amount.new(nice_num, amount.symbol) 111 | end 112 | 113 | # 0.13, 0.20, 0.01 --> 0.19 114 | # 4.13, 0.50, 0 4.50 115 | # 87, 5, 1 89 116 | # 213, 50, 0 250 117 | def round amount, precision, notch = 0 118 | (amount / precision).ceil * precision - notch 119 | end 120 | 121 | # Legacy adapter! Returns mock object for legacy assets from BTS 0.9.x 122 | def self.legacy_asset id 123 | map = { 124 | 0 => ['BTS', 5], 125 | 22 => ['USD', 4], 126 | 14 => ['CNY', 4], 127 | 7 => ['GOLD', 6], 128 | 6 => ['SILVER', 4], 129 | 21 => ['EUR', 4]} 130 | 131 | {'symbol' => map[id][0], 'precision' => map[id][1]} if map[id] 132 | end 133 | 134 | 135 | end 136 | -------------------------------------------------------------------------------- /app/models/asset.rb: -------------------------------------------------------------------------------- 1 | class Asset 2 | 3 | VALID = %w{USD GOLD SILVER CNY EUR BTS} 4 | 5 | def self.valid? sym 6 | VALID.include? sym 7 | end 8 | 9 | def initialize asset 10 | @asset = asset.to_s.upcase 11 | raise "Unknown asset: #{asset}" unless Asset.valid?(asset) 12 | end 13 | 14 | def self.rate from, to, amt = 1 15 | r = case 16 | when from == to then 1.0 17 | when from == 'BTS' then feed(to) 18 | when to == 'BTS' then 1 / feed(from) 19 | else feed(to).to_f / feed(from).to_f 20 | end 21 | end 22 | 23 | private 24 | def self.feed asset 25 | if @last.nil? || @last < 5.seconds.ago 26 | @price = {} 27 | @last = DateTime.now 28 | end 29 | @price[asset] ||= Graphene::API::RPC.instance.median_feed_price asset #rescue raise("Asset `#{asset}` ERROR") 30 | end 31 | 32 | end 33 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roadscape/cryptofresh/af471800ca5a2e21c5a311fd38839e49f0f79774/app/models/concerns/.keep -------------------------------------------------------------------------------- /app/models/order.rb: -------------------------------------------------------------------------------- 1 | class Order < ActiveRecord::Base 2 | belongs_to :product 3 | belongs_to :shipping 4 | 5 | validates_presence_of :product_id, :bts_amount, :bts_asset_id 6 | validates_presence_of :shipping_id, :address, :unless => :is_dd? 7 | 8 | before_validation :add_total, :set_pub_id, :check_stock 9 | before_create :set_due_at 10 | 11 | def to_param 12 | pub_id 13 | end 14 | 15 | def self.from_param pub_id 16 | where(:pub_id => pub_id).order('id DESC').first or raise ActiveRecord::RecordNotFound.new("Couldn't find order #{pub_id}") 17 | end 18 | 19 | 20 | 21 | def is_dd? 22 | product.dl.exists? 23 | end 24 | 25 | def expired? 26 | !paid? && (due_at < DateTime.now) 27 | end 28 | 29 | def stale? 30 | paid? ? paid_at < 1.day.ago : expired? 31 | end 32 | 33 | def paid? 34 | !paid_at.nil? 35 | end 36 | 37 | def bts_url 38 | "bts:#{Wallet::ACCT}/transfer/amount/#{amount.amount}/memo/#{pub_id}/asset/#{amount.symbol}" 39 | end 40 | 41 | def amount 42 | Amount.from_bts({'amount' => bts_amount, 'asset_id' => bts_asset_id}) 43 | end 44 | def amount=(amt) 45 | self.bts_amount = amt.bts_amount 46 | self.bts_asset_id = amt.bts_asset_id 47 | end 48 | 49 | 50 | 51 | def set_due_at #before_create 52 | self.due_at = 15.minutes.from_now 53 | end 54 | 55 | def check_stock #before_validation 56 | errors.add(:product_id, "out of stock!") if product.sold_out? 57 | end 58 | 59 | def add_total #before_validation 60 | raise "No product selected" unless product 61 | raise "No asset selected" unless bts_asset_id 62 | amt = product.amount.convert(bts_asset_id) 63 | amt = amt.add(shipping.amount.convert(bts_asset_id)) if shipping 64 | self.amount = amt 65 | end 66 | 67 | def set_pub_id #before_validation 68 | return if pub_id 69 | opts = %w{a x z t r k b e d q w e y u s p t 3 2 6 8} 70 | self.pub_id = (1..4).map{|i| opts.sample}.join.upcase 71 | end 72 | 73 | end 74 | -------------------------------------------------------------------------------- /app/models/photo.rb: -------------------------------------------------------------------------------- 1 | class Photo < ActiveRecord::Base 2 | has_attached_file :image, 3 | :styles => { :medium => "450x300>", :thumb => "100x100>" }, 4 | :url => "/system/:class/:attachment/:id_partition/:style_:hash.:extension", 5 | :hash_data => ":class/:attachment/:id/:style", 6 | :hash_secret => "thisisabitsharesstore2" 7 | #:default_url => "/images/:style/missing.png" 8 | validates_attachment_content_type :image, :content_type => /\Aimage\/.*\Z/ 9 | 10 | belongs_to :product 11 | 12 | validates_presence_of :product 13 | 14 | end 15 | -------------------------------------------------------------------------------- /app/models/product.rb: -------------------------------------------------------------------------------- 1 | class Product < ActiveRecord::Base 2 | 3 | has_attached_file :dl, 4 | :styles => {}, 5 | :url => "/system/:class/:attachment/:id_partition/:style_:hash.:extension", 6 | :hash_data => ":class/:attachment/:id/:style", 7 | :hash_secret => STORE['secret_phrase'] 8 | 9 | # .mobi = application/octet-stream 10 | validates_attachment :dl, content_type: { content_type: ["application/pdf", "image/jpeg", "application/octet-stream", "application/epub+zip"] } 11 | 12 | has_attached_file :image, 13 | :styles => { :medium => "300x300>", :thumb => "100x100>" }, 14 | :url => "/system/:class/:attachment/:id_partition/:style_:hash.:extension", 15 | :hash_data => ":class/:attachment/:id/:style", 16 | :hash_secret => STORE['secret_phrase'] 17 | validates_attachment_content_type :image, :content_type => /\Aimage\/.*\Z/ 18 | 19 | has_many :orders 20 | has_many :photos 21 | 22 | accepts_nested_attributes_for :photos, :reject_if => lambda { |t| t['image'].blank? }, :allow_destroy => true 23 | 24 | #self.inheritance_column = :node_type 25 | 26 | def num_sold 27 | orders.select{|o| o.paid?}.size 28 | end 29 | 30 | def num_locked 31 | orders.select{|o| !o.paid? && !o.expired?}.size 32 | end 33 | 34 | def num_left 35 | return 9999 unless num_stock 36 | (num_stock - num_sold) 37 | end 38 | 39 | def sold_out? 40 | return false unless num_stock 41 | num_left <= 0 42 | end 43 | 44 | def all_images 45 | #self_and_descendants.select{|p| p.image.exists?}.map{|p| p.image} 46 | out = [] 47 | out << image if image.exists? 48 | out += photos.map(&:image) 49 | out 50 | photos.any? ? photos.map(&:image) : (image.exists? ? [image] : []) 51 | end 52 | 53 | def priced? 54 | !cents.nil? 55 | end 56 | 57 | def amount 58 | Amount.cent(cents) if cents 59 | end 60 | 61 | def new_order 62 | Order.new(:product_id => id) 63 | end 64 | 65 | def self.categories 66 | Product.where(:parent_id => nil).order(:position) 67 | end 68 | 69 | def root? 70 | parent_id.nil? 71 | end 72 | 73 | def root 74 | parent_id ? Product.find(parent_id) : self 75 | end 76 | 77 | def depth 78 | return 0 unless parent_id 79 | 1 + Product.find(parent_id).depth 80 | end 81 | 82 | def children 83 | Product.where(:parent_id => id) 84 | end 85 | 86 | def self_and_descendants 87 | [self] + children.map{|c| c.self_and_descendants}.flatten 88 | end 89 | end 90 | -------------------------------------------------------------------------------- /app/models/shipping.rb: -------------------------------------------------------------------------------- 1 | class Shipping < ActiveRecord::Base 2 | def amount 3 | Amount.cent(cents) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/models/subscription.rb: -------------------------------------------------------------------------------- 1 | class Subscription < ActiveRecord::Base 2 | validates_presence_of :email 3 | end 4 | -------------------------------------------------------------------------------- /app/models/tx.rb: -------------------------------------------------------------------------------- 1 | class Tx < ActiveRecord::Base 2 | validates_presence_of :block_num, :trx_id, :json 3 | validates_numericality_of :block_num 4 | 5 | # Given a single transaction-row from the API, 6 | # make sure we have it saved, and 7 | # return it in a Tx object wrapper 8 | def self.cache(tx_data) 9 | trx_id = tx_data['op']['id'] 10 | block_num = tx_data['op']['block_num'] 11 | 12 | Tx.find_by_trx_id(trx_id) || Tx.create!( 13 | :block_num => block_num, 14 | :trx_id => trx_id, 15 | :json => tx_data.to_json) 16 | end 17 | 18 | def raw 19 | @raw ||= JSON::parse(json) 20 | end 21 | 22 | def raw=(hash) 23 | @raw = hash 24 | self.json = hash.to_json 25 | end 26 | 27 | # This method is really paranoid and will hopefully break right away 28 | # if any of my assumptions about the API interface are broken. 29 | def ledger_entry 30 | tx = raw 31 | 32 | return ledger_entry_legacy if legacy? 33 | 34 | memo = tx.delete('memo') 35 | desc = tx.delete('description') 36 | opx = tx.delete('op') 37 | raise "UNHANDLED DATA: #{tx}" unless tx.empty? 38 | 39 | id = opx['id'] 40 | block = opx['block_num'] 41 | code = opx['op'][0] 42 | op = opx['op'][1] 43 | raise "UNKNOWN CODE: #{code}--#{op}" unless code == 0 44 | 45 | # Clean up the keys. Build a minimal hash of reference data. 46 | op.delete('memo') 47 | op.delete('extensions') 48 | date = Graphene::API::RPC.instance.get_block(block)['timestamp'] 49 | ref = { 50 | 'trx_id' => id, 51 | 'block_num' => block, 52 | 'memo' => memo, 53 | 'date' => bts_date(date), 54 | 'expires' => bts_date(date), 55 | 'fee' => op.delete('fee'), 56 | 'amount' => op.delete('amount'), 57 | 'from' => op.delete('from'), 58 | 'to' => op.delete('to')} 59 | 60 | # The only remaining key is removed. The hash should be empty! 61 | raise "UNHANDLED DATA: #{op}" unless op.empty? 62 | 63 | ref 64 | end 65 | 66 | private 67 | 68 | def bts_date ts 69 | DateTime.parse(ts).to_time.localtime("-06:00").strftime("%Y-%m-%d %H:%M:%S") 70 | end 71 | 72 | def legacy? 73 | !raw['is_market'].nil? 74 | end 75 | 76 | # Legacy adapter! Any BTS 0.9x transactions are fed thru here. 77 | def ledger_entry_legacy 78 | tx = raw 79 | 80 | # warn that we threw away ledger entries [1..-1] (rare. ok for legacy compat) 81 | multi = (tx['ledger_entries'].size != 1) 82 | note = " #{multi ? '[WARN]' : ''}[LEGACY]" 83 | 84 | { 'date' => bts_date(tx['timestamp']), 85 | 'expires' => bts_date(tx['expiration_timestamp']), 86 | 'fee' => tx['fee'], 87 | 'trx_id' => tx['trx_id'], 88 | 'block_num' => tx['block_num'], 89 | 'from' => tx['ledger_entries'][0]['from_account'], 90 | 'to' => tx['ledger_entries'][0]['to_account'], 91 | 'amount' => tx['ledger_entries'][0]['amount'], 92 | 'memo' => tx['ledger_entries'][0]['memo'] + note } 93 | end 94 | 95 | end 96 | -------------------------------------------------------------------------------- /app/models/wallet.rb: -------------------------------------------------------------------------------- 1 | class Wallet 2 | ACCT = STORE['cashier_acct'] 3 | 4 | # Checks all open orders vs all incoming payments 5 | def self.scan 6 | # Get all ledger entries that transfer to our cashier account 7 | entries = [] 8 | 9 | Graphene::API::RPC.instance.get_account_history(ACCT, 100000).each do |tx_data| 10 | tx = Tx.cache(tx_data) 11 | entry = tx.ledger_entry 12 | entries << entry if entry['to'] == ACCT && entry['from'] != ACCT 13 | end 14 | 15 | # For each tx, find the matching unpaid order, and check it pays in full 16 | open_orders = Order.where(:paid_at => nil).where('due_at > ?', 2.hours.ago) 17 | entries.each do |tx| 18 | order = open_orders.find{|o| tx['memo'].include?(o.pub_id)} 19 | next unless order 20 | next unless Amount.from_bts(tx['amount']).includes?(order.amount) 21 | order.update_attributes(:paid_at => DateTime.now, :trx_id => tx['trx_id']) 22 | end 23 | end 24 | 25 | 26 | 27 | end 28 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= @page_title || 'cryptofresh' %> 5 | 6 | <%= stylesheet_link_tag 'application', media: 'all' %> 7 | <%= javascript_include_tag 'application' %> 8 | <%= csrf_meta_tags %> 9 | 10 | 11 | 12 |
13 | 14 | <% if params[:controller] == 'products' %> 15 |
16 | <%= render 'shared/asset_select' %> 17 |
18 | <% end %> 19 | 20 | <%= render 'shared/header' %> 21 | <%= render 'shared/admin' if params['controller'] == 'nodes' %> 22 | <%= render 'shared/flash' %> 23 | <%= render 'shared/network_alerts' %> 24 | 25 | <%= yield %> 26 | 27 |
28 | 31 | 32 |
33 | 34 | <%= render :partial => 'shared/ga' %> 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /app/views/orders/show.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 | <%= image_tag @order.product.root.image(:medium), :class => 'img-thumbnail-old', :style => "max-height: 240px; max-width: 190px;" %> 6 |
7 |
8 | 9 |
10 |

<%= @order.product.root.name %> <%= @order.product.name %>

11 |
12 | 13 |
14 |
15 |

16 | <%= image_tag '/cashier.jpg', :class => 'pull-left img-thumbnail', :style => "max-height: 120px; margin-right: 16px;" %> 17 | To complete your order, 18 | send <%= @order.amount.short.gsub(' ', ' ').html_safe %> 19 | to <%= Wallet::ACCT %> with 20 | memo <%= @order.pub_id %>. 21 |

22 |

23 | <%= link_to 'Pay with BitShares client', @order.bts_url, :class => 'btn btn-primary pull-right', :id => 'pay_button' %> 24 | 25 |

26 |
27 | 28 |
29 |

30 | <% if @order.shipping %> 31 | Item will be shipped to:
32 | <%= @order.address.gsub(/[\r\n]+/, ", ") %>, 33 | <%= @order.shipping.name %> 34 | <%# local_price(@order.shipping) %> 35 | <% else %> 36 | Your download will begin within 10 seconds of payment. 37 | <% end %> 38 |

39 |
40 | 41 | 42 |
43 |
44 | 45 | 46 | 110 | -------------------------------------------------------------------------------- /app/views/orders/stale.html.erb: -------------------------------------------------------------------------------- 1 | This order has expired, is stale (completed more than 24h ago), or does not exist. 2 | -------------------------------------------------------------------------------- /app/views/products/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= form_for @product, :html => {:role => 'form', :style => "width: 800px;", :multipart => true} do |f| %> 2 | 3 | <% if @product.errors.any? %> 4 | Oops! 5 | 10 | <% end %> 11 | 12 | 19 | 20 |
21 | <%= f.label :name %>
22 | <%= f.text_field :name, :size => 40, :class => 'xform-control' %> 23 |
24 | 25 |
26 | <%= f.label :parent_id, "Parent/Category" %>
27 | <%= f.select :parent_id, Product.all.reject{|p| p.depth > 0}.map{|p| [p.name, p.id]}, :include_blank => "(top-level)" %> 28 |
29 | 30 |
31 | <%= f.label :position, "Sort order (1..n)" %>
32 | <%= f.text_field :position %> 33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | <% if @product.root? %> 41 | 42 | 43 | 44 | 45 | 46 | 47 |
48 | <%= f.label :button_label %>
49 | <%= f.text_field :button_label %> 50 |
51 |
52 | <%= f.label :short_desc, "Short Description (HTML)" %>
53 | <%= f.text_area :short_desc, :class => 'xform-control', :rows => 3, :cols => 100 %> 54 |
55 |
56 | <%= f.label :desc, "Description (HTML)" %>
57 | <%= f.text_area :desc, :class => 'xform-control', :rows => 6, :cols => 100 %> 58 |
59 | 60 |
61 |
62 | <%= f.label :image %> 63 | <%= f.file_field :image, :class => 'form-control' %> 64 |
65 | <% if @product.image.exists? %> 66 | <%= image_tag @product.image(:thumb), :class => 'img-thumbnail' %> this is the front page/feature image. if you upload another it gets replaced. 67 | <% end %> 68 |
69 | 70 | <% [1, 5-@product.photos.size].max.times{@product.photos.build} %> 71 | 72 |
73 | <%= f.label :photos %>
74 | <%= f.fields_for :photos do |f2| %> 75 |
76 | <% if f2.object.image.exists? %> 77 |
78 | <%= image_tag f2.object.image(:thumb), :class => 'img-thumbnails' %> 79 |

80 | <%= f2.check_box :_destroy %> Destroy? 81 | <% else %> 82 |
83 | <%= f2.file_field :image, :class => 'form-control' %> 84 | <% end %> 85 | <%= f2.hidden_field :id %> 86 |
87 | <% end %> 88 |
89 | 90 | 91 | 92 | 93 | <% else %> 94 | 95 | 96 | 97 | 98 | <% unless @product.dl.exists? %> 99 |
100 | <%= f.label :num_stock, "Stock count" %>
101 | <%= f.text_field :num_stock %> 102 | Sold: <%= @product.num_sold %> Locked: <%= @product.num_locked %> 103 |
104 | <% end %> 105 | 106 |
107 | <%= f.label :cents %>
108 | <%= f.number_field :cents, :class => 'xform-control' %> 109 |
110 | 111 |
112 |
113 | <%= f.label :dl, "Digital Download (PDF only)" %> 114 | <%= f.file_field :dl, :class => 'form-control' %> 115 |
116 | <% if @product.dl.exists? %> 117 |

ACHTUNG! There is an existing digital download: <%= @product.dl_file_name %>. Uploading will replace this file.

118 | <% end %> 119 |
120 | 121 | 122 | 123 | 124 | <% end %> 125 | 126 | 127 | 128 | 129 | 130 | <%= f.submit 'Save Changes', :class => 'btn btn-success' %> 131 | 132 | <% end %> 133 | 134 | -------------------------------------------------------------------------------- /app/views/products/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Edit Product

2 | 3 |
4 | 5 | <% 6 | def list(parent_id = nil) 7 | out = [] 8 | Product.where(:parent_id => parent_id).order('position').each do |h| 9 | link = link_to_unless(@product == h, h.name, edit_product_url(h)) 10 | sold = case 11 | when h.num_stock then "#{h.num_sold} of #{h.num_stock}" 12 | when h.num_sold > 0 then "#{h.num_sold}" 13 | else "" 14 | end 15 | out << "
  • #{sold}
    #{link} #{list h.id}
  • " 16 | end 17 | "" unless out.size == 0 18 | end 19 | %> 20 | 21 |
    22 |
    Sold
    23 |
    24 | <%= list.html_safe %> 25 |
    26 | 27 | <%= render :partial => 'form' %> 28 | -------------------------------------------------------------------------------- /app/views/products/index.html.erb: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 |
    15 | 16 | <% @products.each do |p| %> 17 |
    18 |
    19 |
    20 |
    21 | <%= link_to image_tag(p.image.url(:medium)), p %> 22 | <%= link_to "", p, :class => 'bg' %> 23 |
    24 |
    25 | 26 |
    27 |

    <%= link_to p.name, p %>

    28 |
    29 | <%= p.short_desc.html_safe %> 30 |
    31 | "> 32 | <% cheapest = p.children.sort_by{|c| c.cents}.first %> 33 | <%= p.children.size > 1 ? 'Starting at' : '' %> <%= local_price(cheapest, true) %> 34 | 35 |

    <%= link_to (!p.button_label.blank? ? p.button_label : 'Check it out →'.html_safe), p, :class => 'btn btn-primary xbtn-lg' %>

    36 |
    37 |
    38 |
    39 | <% end %> 40 | 41 |
    42 |
    43 |
    44 |
    45 | <%= image_tag "/mystery.jpg" %> 46 |
    47 |
    48 |
    49 | 50 |
    51 |

    More coming soon

    52 |
    53 |

    More BitShares swag will be here soon!

    54 |
    55 | For updates check BitSharesTalk, or sign up below:
    56 | <%= form_for :subscription, :url => subscribe_products_url, :html => {:class => 'form-inline', :role => 'form'} do |f| %> 57 | <%= f.text_field :email, :size => 32, :placeholder => "satoshi@gmail.com", :class => 'form-control' %> 58 | <%= f.submit "Get updates", :class => 'btn btn-primary' %> 59 | <% end %> 60 |
    61 |
    62 |
    63 | 64 |
    65 | -------------------------------------------------------------------------------- /app/views/products/new.html.erb: -------------------------------------------------------------------------------- 1 |

    New Product

    2 | 3 | <%= render :partial => 'form' %> 4 | -------------------------------------------------------------------------------- /app/views/products/report.html.erb: -------------------------------------------------------------------------------- 1 | 4 | 5 |

    Transactions

    6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | <% 18 | 19 | ids = Hash[Order.all.map{|o| [o.pub_id, o]}] 20 | Tx.all.sort_by{|tx| tx.block_num}.reverse.each do |tx| 21 | tx = tx.ledger_entry 22 | id = ids.keys.find{|i| tx['memo'].include?(i)} 23 | o = id ? ids[id] : nil 24 | status = case 25 | when !o then "" 26 | when (Amount.from_bts(tx['amount']).includes?(o.amount) rescue false) then "PAID" 27 | else "INVALID" 28 | end 29 | paid = status == "PAID" 30 | %> 31 | 32 | 33 | 34 | 35 | 41 | 42 | <% memo = tx['memo'] %> 43 | <% memo = o ? memo.gsub(o.pub_id, "\\0") : "#{memo} " %> 44 | 46 | 47 | 48 | <% end %> 49 |
    trxblockdateamount feefrom → tostatusmemo
    <%= tx['trx_id'][0..8] %><%= tx['block_num'] %><%= tx['date'] %> 36 | 37 | <%= Amount.from_bts(tx['fee']).short %> 38 | 39 | <%= Amount.from_bts(tx['amount']).short %> 40 | <%= "#{tx['from'].gsub('cashier as cashier', 'cashier')} → #{tx['to']}".gsub(Wallet::ACCT, "\\0").html_safe %><%= (paid ? "PAID" : "invalid").html_safe %> 45 | <%= memo.html_safe %>
    50 | 51 |
    52 |
    53 | 54 |

    Orders

    55 | 56 | <%= DateTime.now %> 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | <% Order.all.order('created_at DESC').reject{|o| o.due_at < 24.hours.ago && !o.paid?}.each do |o| %> 72 | <% 73 | status = case 74 | when o.paid? then '' 75 | when o.expired? then 'Expired' 76 | else "DUE in #{((o.due_at - DateTime.now)/60).round(1)}m" 77 | end 78 | %> 79 | 80 | 81 | 82 | 83 | 84 | 88 | <% tx = Tx.where(:trx_id => o.trx_id).first %> 89 | 90 | 91 | 92 | 93 | 94 | <% end %> 95 | 96 |
    idIDproductpricepaid?paid bycreatedshippingip, referer
    <%= o.id %><%= o.pub_id %><%= "#{o.product.root.name}
    #{o.product.name}".html_safe rescue 'Unknown' %>
    <%= o.amount.short rescue '??' %> 85 | <%= o.paid_at.strftime("%Y-%m-%d %H:%M") rescue '' %> 86 | <%= status %> 87 | <%= (tx ? tx.ledger_entry['from'] : '') %><%= o.created_at.strftime("%Y-%m-%d %H:%M") %><%= h(o.address).gsub("\n","
    ").html_safe %>
    <%= o.ip %>
    <%= o.referrer_acct %>
    97 | 98 | 99 | -------------------------------------------------------------------------------- /app/views/products/show.html.erb: -------------------------------------------------------------------------------- 1 | 11 | 12 | <% @root = @product.root %> 13 | 14 | <%= form_for @order, :url => order_product_url(@product), :html => {:role => :form} do |f| %> 15 |
    16 |
    17 | <% imgs = @root.all_images %> 18 |
    19 | <%= image_tag imgs[0].url(:medium), :id => 'main_image' %> 20 |
    21 | <% imgs.each do |img| %> 22 | <%= link_to image_tag(img.url(:thumb), :class => 'img-thumbnail'), img.url(:medium) %> 23 | <% end %> 24 |
    25 | 36 | 37 |
    38 |

    <%= @root.name %>

    39 | <%= (@root.desc.blank? ? @root.short_desc : @root.desc).html_safe rescue '' %> 40 | <% if @order.errors.any? %> 41 | <%= @order.errors.full_messages.join(' ') %> 42 | <% end %> 43 | 44 | <%= f.hidden_field :product_id %> 45 | <% 46 | rc = @root.children 47 | type = (@order.is_dd? ? "digital download" : "style") 48 | type = "select a #{type}:" if @product.root? 49 | %> 50 | <%= f.label :product_id, type, :class => (@product.root? ? "text-danger" : nil) %>
    51 | <% unless @product.root? %> 52 | <%= "#{@product.name} (#{local_price(@product)})" %> 53 | <%= link_to 'change', @root unless rc.size == 1 %> 54 | <% else %> 55 | <% rc.each do |p| %> 56 | <%= link_to p.name, p, :class => "xvariant" %> 57 | <% if p.num_left < 1 %> 58 | Sold out! 59 | <% else %> 60 | <%= local_price(p) %> 61 | <% end %> 62 |
    63 | <% end %> 64 | <% end %> 65 |

    66 | 67 | <% if @order.product.priced? %> 68 |
    69 |
    70 | <% unless @order.is_dd? %> 71 | <%= f.label :address, "ship to" %> 72 | <%= f.text_area :address, :rows => 3, :cols => 22, :placeholder => "Satoshi Nakamoto\n1101 Cipher St\nBlacksburg, VA", :class => 'form-control' %> 73 | <%= f.label :shipping_id, "shipping" %> 74 | <%= f.select :shipping_id, Hash[Shipping.all.map{|s| ["#{s.name.downcase} (#{local_price(s)})", s.id]}], {:include_blank => false}, {:class => 'form-control'} %> 75 | <%= f.submit "Pay with Bit#{session[:asset]} →".html_safe, :class => 'btn btn-primary btn-lg' %> 76 | <% else %> 77 | <%= f.button "#{local_price(@product, true)} →".html_safe, :type => 'submit', :class => 'btn btn-primary btn-lg' %> 78 | <% end %> 79 | <% if session[:asset] == 'USD' %> 80 |
    81 |
    82 | <%= link_to "What is BitUSD?", "http://whatisbitusd.com/", :class => 'small', :rel => 'nofollow' %> 83 | <% end %> 84 |
    85 |
    86 | <% end %> 87 | 88 |
    89 | 90 | <%# link_to "Edit", edit_product_url(@product) %> 91 |
    92 | <% end %> 93 | -------------------------------------------------------------------------------- /app/views/shared/_asset_select.html.erb: -------------------------------------------------------------------------------- 1 | <% ['USD', 'CNY', 'GOLD', 'SILVER', 'EUR'].each do |asset| %> 2 | <% classes = "asset bts-bg-#{asset.downcase}" %> 3 | <% name = "#{asset == 'BTS' ? 'BTS' : 'Bit'+asset}".html_safe %> 4 | <%= link_to_unless(asset == session[:asset], name, "?asset=#{asset}", :class => classes){"#{name}".html_safe} %> 5 | <% end %> 6 | proudly accepted 7 | -------------------------------------------------------------------------------- /app/views/shared/_flash.html.erb: -------------------------------------------------------------------------------- 1 | <% flash.each do |type, message| %> 2 |
    3 | 4 | <%= message %> 5 |
    6 | <% end %> 7 | -------------------------------------------------------------------------------- /app/views/shared/_ga.html.erb: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /app/views/shared/_header.html.erb: -------------------------------------------------------------------------------- 1 | <% cls = (params[:controller] == 'products' && params[:action] == 'index') ? "" : " mini" %> 2 | <% cls = "" if (params[:controller] == 'nodes' && params[:action] == 'root') %> 3 | <% cls = "" if @node && @node.root? && params[:action] == 'view' %> 4 |
    5 | 6 | <% if params[:controller] == 'products' %> 7 | # tangible assets for the blockchain age.    8 | <% else %> 9 | # tangible assets for the blockchain age.    10 | <% end %> 11 | 12 | <% if STORE['app_env'] == 'dev' %> 13 |
    14 | DEV 15 |
    16 | <% end %> 17 |
    18 | 19 | 20 | -------------------------------------------------------------------------------- /app/views/shared/_network_alerts.html.erb: -------------------------------------------------------------------------------- 1 | <% pct = Graphene::API::RPC.instance.info['participation'].to_i %> 2 | <% if pct < 80 %> 3 |
    4 | Warning: Delegate participation rate is <%= pct %>%. Sales are temporarily unavailable. 5 |
    6 | <% end %> 7 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path("../spring", __FILE__) 4 | rescue LoadError 5 | end 6 | APP_PATH = File.expand_path('../../config/application', __FILE__) 7 | require_relative '../config/boot' 8 | require 'rails/commands' 9 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path("../spring", __FILE__) 4 | rescue LoadError 5 | end 6 | require_relative '../config/boot' 7 | require 'rake' 8 | Rake.application.run 9 | -------------------------------------------------------------------------------- /bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This file loads spring without using Bundler, in order to be fast 4 | # It gets overwritten when you run the `spring binstub` command 5 | 6 | unless defined?(Spring) 7 | require "rubygems" 8 | require "bundler" 9 | 10 | if match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m) 11 | ENV["GEM_PATH"] = ([Bundler.bundle_path.to_s] + Gem.path).join(File::PATH_SEPARATOR) 12 | ENV["GEM_HOME"] = "" 13 | Gem.paths = ENV 14 | 15 | gem "spring", match[1] 16 | require "spring/binstub" 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run Rails.application 5 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | require 'rails/all' 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | module Store 10 | class Application < Rails::Application 11 | # Settings in config/environments/* take precedence over those specified here. 12 | # Application configuration should go into files in config/initializers 13 | # -- all .rb files in that directory are automatically loaded. 14 | 15 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 16 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 17 | config.time_zone = 'Central Time (US & Canada)' 18 | #config.active_record.default_timezone = 'Central Time (US & Canada)' 19 | 20 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 21 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 22 | # config.i18n.default_locale = :de 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | # Set up gems listed in the Gemfile. 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | 4 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) 5 | -------------------------------------------------------------------------------- /config/database.yml.example: -------------------------------------------------------------------------------- 1 | default: &default 2 | adapter: mysql2 3 | user: root 4 | database: cryptofresh 5 | 6 | development: 7 | <<: *default 8 | 9 | production: 10 | <<: *default 11 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports and disable caching. 13 | config.consider_all_requests_local = true 14 | config.action_controller.perform_caching = false 15 | 16 | # Don't care if the mailer can't send. 17 | config.action_mailer.raise_delivery_errors = false 18 | 19 | # Print deprecation notices to the Rails logger. 20 | config.active_support.deprecation = :log 21 | 22 | # Raise an error on page load if there are pending migrations. 23 | config.active_record.migration_error = :page_load 24 | 25 | # Debug mode disables concatenation and preprocessing of assets. 26 | # This option may cause significant delays in view rendering with a large 27 | # number of complex assets. 28 | config.assets.debug = true 29 | 30 | # Adds additional error checking when serving assets at runtime. 31 | # Checks for improperly declared sprockets dependencies. 32 | # Raises helpful error messages. 33 | config.assets.raise_runtime_errors = true 34 | 35 | # Raises error for missing translations 36 | # config.action_view.raise_on_missing_translations = true 37 | end 38 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = false #true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Enable Rack::Cache to put a simple HTTP cache in front of your application 18 | # Add `rack-cache` to your Gemfile before enabling this. 19 | # For large-scale production use, consider using a caching reverse proxy like nginx, varnish or squid. 20 | # config.action_dispatch.rack_cache = true 21 | 22 | # Disable Rails's static asset server (Apache or nginx will already do this). 23 | config.serve_static_assets = false 24 | 25 | # Compress JavaScripts and CSS. 26 | config.assets.js_compressor = :uglifier 27 | # config.assets.css_compressor = :sass 28 | 29 | # Do not fallback to assets pipeline if a precompiled asset is missed. 30 | config.assets.compile = false 31 | 32 | # Generate digests for assets URLs. 33 | config.assets.digest = true 34 | 35 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 36 | 37 | # Specifies the header that your server uses for sending files. 38 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache 39 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx 40 | 41 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 42 | # config.force_ssl = true 43 | 44 | # Set to :debug to see everything in the log. 45 | config.log_level = :info 46 | 47 | # Prepend all log lines with the following tags. 48 | # config.log_tags = [ :subdomain, :uuid ] 49 | 50 | # Use a different logger for distributed setups. 51 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) 52 | 53 | # Use a different cache store in production. 54 | # config.cache_store = :mem_cache_store 55 | 56 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 57 | # config.action_controller.asset_host = "http://assets.example.com" 58 | 59 | # Ignore bad email addresses and do not raise email delivery errors. 60 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 61 | # config.action_mailer.raise_delivery_errors = false 62 | 63 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 64 | # the I18n.default_locale when a translation cannot be found). 65 | config.i18n.fallbacks = true 66 | 67 | # Send deprecation notices to registered listeners. 68 | config.active_support.deprecation = :notify 69 | 70 | # Disable automatic flushing of the log to improve performance. 71 | # config.autoflush_log = false 72 | 73 | # Use default logging formatter so that PID and timestamp are not suppressed. 74 | config.log_formatter = ::Logger::Formatter.new 75 | 76 | # Do not dump schema after migrations. 77 | config.active_record.dump_schema_after_migration = false 78 | end 79 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure static asset server for tests with Cache-Control for performance. 16 | config.serve_static_assets = true 17 | config.static_cache_control = 'public, max-age=3600' 18 | 19 | # Show full error reports and disable caching. 20 | config.consider_all_requests_local = true 21 | config.action_controller.perform_caching = false 22 | 23 | # Raise exceptions instead of rendering exception templates. 24 | config.action_dispatch.show_exceptions = false 25 | 26 | # Disable request forgery protection in test environment. 27 | config.action_controller.allow_forgery_protection = false 28 | 29 | # Tell Action Mailer not to deliver emails to the real world. 30 | # The :test delivery method accumulates sent emails in the 31 | # ActionMailer::Base.deliveries array. 32 | config.action_mailer.delivery_method = :test 33 | 34 | # Print deprecation notices to the stderr. 35 | config.active_support.deprecation = :stderr 36 | 37 | # Raises error for missing translations 38 | # config.action_view.raise_on_missing_translations = true 39 | end 40 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Precompile additional assets. 7 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. 8 | # Rails.application.config.assets.precompile += %w( search.js ) 9 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /config/initializers/bitshares.rb: -------------------------------------------------------------------------------- 1 | STORE = YAML.load_file('config/store.yml') 2 | 3 | require Rails.root.join("lib/graphene_api.rb").to_s 4 | Graphene::API::RPC.init(STORE['rpc_url']) 5 | -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.action_dispatch.cookies_serializer = :json -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: '_store_session' 4 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] if respond_to?(:wrap_parameters) 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | 3 | resources :products do 4 | member do 5 | post "order" => 'products#order', :as => :order 6 | get "new" => 'products#new', :as => :new_child 7 | end 8 | collection do 9 | get 'report' 10 | post 'subscribe' 11 | end 12 | end 13 | 14 | resources :orders do 15 | member do 16 | get "status" 17 | get "download" 18 | end 19 | end 20 | 21 | root 'products#index' 22 | 23 | end 24 | -------------------------------------------------------------------------------- /config/secrets.yml.example: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rake secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | development: 14 | secret_key_base: 9f45b4a7616a75d5a852de64ab0a5724139536c45cd2215f6a592f7695f763f7385846475f633f45f41474968db1a23da99b18f9d398b33db22fa49f03b8090f 15 | 16 | test: 17 | secret_key_base: 29bddd68f328ab06ba03988b4fc0424fac226800e89514bdb8d54f8fbf8e7758001612249519c048f1cc4193daee980e0273eb490514acd95a1a72bd960437ef 18 | 19 | # Do not keep production secrets in the repository, 20 | # instead read values from the environment. 21 | production: 22 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 23 | -------------------------------------------------------------------------------- /config/store.yml.example: -------------------------------------------------------------------------------- 1 | cashier_acct: cashier 2 | rpc_url: http://127.0.0.1:8092/rpc 3 | rpc_port: 4 | rpc_user: 5 | rpc_pass: 6 | admin_user: 7 | admin_pass: 8 | secret_phrase: 9 | ga_pub_id: 10 | app_env: dev 11 | -------------------------------------------------------------------------------- /config/unicorn.rb.example: -------------------------------------------------------------------------------- 1 | # set path to app that will be used to configure unicorn, 2 | # note the trailing slash in this example 3 | @dir = "/home/ddd/" 4 | 5 | worker_processes 2 6 | working_directory @dir 7 | 8 | timeout 1800 9 | 10 | # Specify path to socket unicorn listens to, 11 | # we will use this in our nginx.conf later 12 | listen "#{@dir}tmp/sockets/unicorn.sock", :backlog => 64 13 | 14 | # Set process id path 15 | pid "#{@dir}tmp/pids/unicorn.pid" 16 | 17 | # Set log file paths 18 | stderr_path "#{@dir}log/unicorn.stderr.log" 19 | stdout_path "#{@dir}log/unicorn.stdout.log" 20 | -------------------------------------------------------------------------------- /db/migrate/20141105025954_create_txes.rb: -------------------------------------------------------------------------------- 1 | class CreateTxes < ActiveRecord::Migration 2 | def change 3 | create_table :txes do |t| 4 | t.integer :block_num 5 | t.string :trx_id 6 | t.text :json 7 | end 8 | 9 | add_index :txes, :block_num 10 | add_index :txes, :trx_id, :unique => true 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20141211061531_create_shippings.rb: -------------------------------------------------------------------------------- 1 | class CreateShippings < ActiveRecord::Migration 2 | def change 3 | create_table :shippings do |t| 4 | t.string :name 5 | t.integer :cents 6 | t.timestamps 7 | end 8 | Shipping.create(:name => 'United States', :cents => 575) 9 | Shipping.create(:name => 'International', :cents => 900) 10 | Shipping.create(:name => 'Mexico/Canada', :cents => 700) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20141211061539_create_products.rb: -------------------------------------------------------------------------------- 1 | class CreateProducts < ActiveRecord::Migration 2 | def change 3 | create_table :products do |t| 4 | t.string :name 5 | t.text :desc 6 | t.integer :position 7 | t.integer :default_id 8 | t.boolean :is_category, :null => false, :default => false 9 | 10 | t.integer :parent_id 11 | t.integer :cents 12 | 13 | t.attachment :dl 14 | t.attachment :image 15 | t.timestamps 16 | end 17 | 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /db/migrate/20141211072149_create_orders.rb: -------------------------------------------------------------------------------- 1 | class CreateOrders < ActiveRecord::Migration 2 | def change 3 | create_table :orders do |t| 4 | t.integer :product_id 5 | t.integer :shipping_id 6 | t.string :address 7 | t.integer :cents 8 | t.datetime :paid_at 9 | t.string :trx_id 10 | t.datetime :due_at 11 | t.string :comments 12 | t.string :pub_id 13 | t.boolean :void, :null => false, :default => false 14 | 15 | t.timestamps 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /db/migrate/20141230210006_create_subscriptions.rb: -------------------------------------------------------------------------------- 1 | class CreateSubscriptions < ActiveRecord::Migration 2 | def change 3 | create_table :subscriptions do |t| 4 | t.string :email 5 | t.datetime :last_email_at 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20141231091412_extras.rb: -------------------------------------------------------------------------------- 1 | class Extras < ActiveRecord::Migration 2 | def change 3 | add_column :products, :code, :string 4 | add_column :products, :short_desc, :string 5 | add_column :products, :button_label, :string 6 | add_column :products, :num_stock, :integer 7 | add_column :products, :num_sold, :integer 8 | add_column :products, :node_type, :string 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20150101202813_final_extras.rb: -------------------------------------------------------------------------------- 1 | class FinalExtras < ActiveRecord::Migration 2 | def change 3 | add_column :orders, :email, :string 4 | add_column :orders, :ip, :string 5 | add_column :orders, :referrer_acct, :string 6 | add_column :txes, :order_id, :integer 7 | add_column :txes, :entries_json, :text 8 | add_column :txes, :comment, :string 9 | 10 | add_column :products, :royalty_acct, :string 11 | add_column :products, :royalty_cents, :integer 12 | add_column :products, :refer_cents, :integer 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20150113091706_add_currencies.rb: -------------------------------------------------------------------------------- 1 | class AddCurrencies < ActiveRecord::Migration 2 | def change 3 | add_column :orders, :bts_amount, :integer 4 | add_column :orders, :bts_asset_id, :string 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20150119184918_create_photos.rb: -------------------------------------------------------------------------------- 1 | class CreatePhotos < ActiveRecord::Migration 2 | def change 3 | create_table :photos do |t| 4 | t.integer :product_id 5 | t.integer :position 6 | t.string :label 7 | 8 | t.timestamps 9 | end 10 | 11 | add_attachment :photos, :image 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | # This file is auto-generated from the current state of the database. Instead 3 | # of editing this file, please use the migrations feature of Active Record to 4 | # incrementally modify your database, and then regenerate this schema definition. 5 | # 6 | # Note that this schema.rb definition is the authoritative source for your 7 | # database schema. If you need to create the application database on another 8 | # system, you should be using db:schema:load, not running all the migrations 9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 10 | # you'll amass, the slower it'll run and the greater likelihood for issues). 11 | # 12 | # It's strongly recommended that you check this file into your version control system. 13 | 14 | ActiveRecord::Schema.define(version: 20150119184918) do 15 | 16 | create_table "orders", force: true do |t| 17 | t.integer "product_id" 18 | t.integer "shipping_id" 19 | t.string "address" 20 | t.integer "cents" 21 | t.datetime "paid_at" 22 | t.string "trx_id" 23 | t.datetime "due_at" 24 | t.string "comments" 25 | t.string "pub_id" 26 | t.boolean "void", default: false, null: false 27 | t.datetime "created_at" 28 | t.datetime "updated_at" 29 | t.string "email" 30 | t.string "ip" 31 | t.string "referrer_acct" 32 | t.integer "bts_amount" 33 | t.string "bts_asset_id" 34 | end 35 | 36 | create_table "photos", force: true do |t| 37 | t.integer "product_id" 38 | t.integer "position" 39 | t.string "label" 40 | t.datetime "created_at" 41 | t.datetime "updated_at" 42 | t.string "image_file_name" 43 | t.string "image_content_type" 44 | t.integer "image_file_size" 45 | t.datetime "image_updated_at" 46 | end 47 | 48 | create_table "products", force: true do |t| 49 | t.string "name" 50 | t.text "desc" 51 | t.integer "position" 52 | t.integer "default_id" 53 | t.boolean "is_category", default: false, null: false 54 | t.integer "parent_id" 55 | t.integer "cents" 56 | t.string "dl_file_name" 57 | t.string "dl_content_type" 58 | t.integer "dl_file_size" 59 | t.datetime "dl_updated_at" 60 | t.string "image_file_name" 61 | t.string "image_content_type" 62 | t.integer "image_file_size" 63 | t.datetime "image_updated_at" 64 | t.datetime "created_at" 65 | t.datetime "updated_at" 66 | t.string "code" 67 | t.string "short_desc" 68 | t.string "button_label" 69 | t.integer "num_stock" 70 | t.integer "num_sold" 71 | t.string "node_type" 72 | t.string "royalty_acct" 73 | t.integer "royalty_cents" 74 | t.integer "refer_cents" 75 | end 76 | 77 | create_table "shippings", force: true do |t| 78 | t.string "name" 79 | t.integer "cents" 80 | t.datetime "created_at" 81 | t.datetime "updated_at" 82 | end 83 | 84 | create_table "subscriptions", force: true do |t| 85 | t.string "email" 86 | t.datetime "last_email_at" 87 | t.datetime "created_at" 88 | t.datetime "updated_at" 89 | end 90 | 91 | create_table "txes", force: true do |t| 92 | t.integer "block_num" 93 | t.string "trx_id" 94 | t.text "json" 95 | t.integer "order_id" 96 | t.text "entries_json" 97 | t.string "comment" 98 | end 99 | 100 | add_index "txes", ["block_num"], name: "index_txes_on_block_num", using: :btree 101 | add_index "txes", ["trx_id"], name: "index_txes_on_tx", unique: true, using: :btree 102 | 103 | end 104 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) 7 | # Mayor.create(name: 'Emanuel', city: cities.first) 8 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roadscape/cryptofresh/af471800ca5a2e21c5a311fd38839e49f0f79774/lib/assets/.keep -------------------------------------------------------------------------------- /lib/graphene_api.rb: -------------------------------------------------------------------------------- 1 | #!/usr/local/rvm/rubies/ruby-2.1.3/bin/ruby 2 | 3 | require 'net/http' 4 | require 'uri' 5 | require 'json' 6 | 7 | # http://docs.bitshares.eu/api/access.html 8 | 9 | module Graphene 10 | 11 | class API 12 | class RPC 13 | @@rpc = nil 14 | 15 | def self.init(url = 'http://127.0.0.1:8092/rpc', username = nil, password = nil) 16 | @@rpc ||= Graphene::API::RPC.new(url, username, password) 17 | end 18 | def self.instance 19 | @@rpc || raise("Not initialized!") 20 | end 21 | def median_feed_price(asset) 22 | rate = get_asset(asset)['options']['core_exchange_rate'] 23 | amount(rate['base']) / amount(rate['quote']) 24 | end 25 | 26 | def initialize(url = 'http://127.0.0.1:8092/rpc', username = nil, password = nil) 27 | @uri = URI(url) 28 | @req = Net::HTTP::Post.new(@uri) 29 | @req.content_type = 'application/json' 30 | @req.basic_auth username, password if username 31 | end 32 | 33 | def self.method_missing(name, *params) 34 | @@rpc.send(name, params) 35 | end 36 | def method_missing(name, *params) 37 | request(name, params) 38 | end 39 | 40 | def request(method, params) 41 | response = nil 42 | body = {jsonrpc: "2.0", method: method, params: params, id: 0} 43 | Net::HTTP.start(@uri.hostname, @uri.port) do |http| 44 | @req.body = body.to_json 45 | response = http.request(@req) 46 | end 47 | result = JSON.parse(response.body) 48 | raise RuntimeError, "called #{method}:#{params} -- #{result['error']}" if result['error'] 49 | result['result'] 50 | end 51 | 52 | protected 53 | def amount(data) 54 | zeroes = get_asset(data['asset_id'])['precision'] 55 | data['amount'].to_f / 10**zeroes 56 | end 57 | end 58 | end 59 | 60 | end 61 | 62 | -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roadscape/cryptofresh/af471800ca5a2e21c5a311fd38839e49f0f79774/lib/tasks/.keep -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    The page you were looking for doesn't exist.

    62 |

    You may have mistyped the address or the page may have moved.

    63 |
    64 |

    If you are the application owner check the logs for more information.

    65 |
    66 | 67 | 68 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    The change you wanted was rejected.

    62 |

    Maybe you tried to change something you didn't have access to.

    63 |
    64 |

    If you are the application owner check the logs for more information.

    65 |
    66 | 67 | 68 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    We're sorry, but something went wrong.

    62 |
    63 |

    If you are the application owner check the logs for more information.

    64 |
    65 | 66 | 67 | -------------------------------------------------------------------------------- /public/bts-10x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roadscape/cryptofresh/af471800ca5a2e21c5a311fd38839e49f0f79774/public/bts-10x16.png -------------------------------------------------------------------------------- /public/bts-15x24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roadscape/cryptofresh/af471800ca5a2e21c5a311fd38839e49f0f79774/public/bts-15x24.png -------------------------------------------------------------------------------- /public/bts-20x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roadscape/cryptofresh/af471800ca5a2e21c5a311fd38839e49f0f79774/public/bts-20x32.png -------------------------------------------------------------------------------- /public/bts-22x24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roadscape/cryptofresh/af471800ca5a2e21c5a311fd38839e49f0f79774/public/bts-22x24.png -------------------------------------------------------------------------------- /public/bts-30x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roadscape/cryptofresh/af471800ca5a2e21c5a311fd38839e49f0f79774/public/bts-30x48.png -------------------------------------------------------------------------------- /public/bts-white-sm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roadscape/cryptofresh/af471800ca5a2e21c5a311fd38839e49f0f79774/public/bts-white-sm.png -------------------------------------------------------------------------------- /public/bts-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roadscape/cryptofresh/af471800ca5a2e21c5a311fd38839e49f0f79774/public/bts-white.png -------------------------------------------------------------------------------- /public/cashier.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roadscape/cryptofresh/af471800ca5a2e21c5a311fd38839e49f0f79774/public/cashier.jpg -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roadscape/cryptofresh/af471800ca5a2e21c5a311fd38839e49f0f79774/public/favicon.ico -------------------------------------------------------------------------------- /public/launch3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roadscape/cryptofresh/af471800ca5a2e21c5a311fd38839e49f0f79774/public/launch3.jpg -------------------------------------------------------------------------------- /public/mystery.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roadscape/cryptofresh/af471800ca5a2e21c5a311fd38839e49f0f79774/public/mystery.jpg -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roadscape/cryptofresh/af471800ca5a2e21c5a311fd38839e49f0f79774/vendor/assets/javascripts/.keep -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/roadscape/cryptofresh/af471800ca5a2e21c5a311fd38839e49f0f79774/vendor/assets/stylesheets/.keep --------------------------------------------------------------------------------