diff --git a/src/api/index.py b/src/api/index.py index a74f99e..a73498f 100755 --- a/src/api/index.py +++ b/src/api/index.py @@ -7,20 +7,25 @@ - Having a free product insures there is no excuse not to signup users - If a product doesn't fall under this model we will find a way to fix it. - + The store must be prefixed with an authentication module, because customers must be signed in before making a purchase. """ from __future__ import division from flask import Flask, request, session, render_template,Response from flask_cors import CORS -import Domain -from User import User -from couchdbkit import Server, Document +# import Domain +# from User import User +# from couchdbkit import Server, Document import stripe import json from StringIO import StringIO import re import os from utils.params import PARAMS -from utils.transport import Couchdb, CouchdbReader +from transport import factory +# from store import Store +import store +from datetime import datetime +# from utils.transport import Couchdb, CouchdbReader PORT = 8100 if 'port' not in PARAMS else int(PARAMS['port']) ; path = PARAMS['path'] #os.environ['CONFIG'] f = open(path) @@ -36,23 +41,71 @@ stripe.api_key = stripe_keys['secret_key'].strip() app = Flask(__name__) -CORS(app) -COUCHDB = Server(uri=CONFIG['couchdb']['uri']) ; -SYS_STORE = CONFIG['couchdb'] -""" - This function will set the user information to the session and update the information - @header uid user email address -""" +# CORS(app) +# COUCHDB = Server(uri=CONFIG['couchdb']['uri']) ; +# SYS_STORE = CONFIG['couchdb'] +SYS_STORE = CONFIG['store'] +@app.route("/") +def index(): + mystore = store.factory.instance(name='music') + products = mystore.get.products() + args = {"context":CONTEXT,"products":products} + return json.dumps(products),"content-type:application/json" + # return render_template("index.html",**args) +@app.route("/json/") +def get_plans_json(id): + """ + This function returns the plans for a given product + """ + HEADER="Content-type:application/json" + try: + mystore = store.factory.instance(name=id) + return json.dumps(mystore.get.plans()),HEADER + except Exception as e: + # + # Log this shit or not + pass + return "[]",HEADER +@app.route("/ui/signup/") +def signup(id): + """ + """ + mystore = store.factory.instance(name=id) + plans = mystore.get.plans() + index = int(request.args['index']) + plan = plans[index] + + args = {"product":id,"label":mystore.product['statement_descriptor'],"plan":plan,"context":CONTEXT,"now":datetime.now().year} + args['theme'] = 'theme-clouds' + return render_template("signup.html",**args) +@app.route("/ui/") +def get_plans_ui(id): + mystore = store.factory.instance(name=id) + # + # sort plans by + + description = mystore.product['metadata']['about'] if 'about' in mystore.product['metadata'] else '' + label = mystore.product['statement_descriptor'] + args = {"product":id,"label":label,"description":description,"context":CONTEXT,"plans":mystore.get.plans(),"now":datetime.now().year} + args['theme'] = 'theme-clouds' + print (mystore.product.keys()) + return render_template('plans.html',**args) @app.route("/init/",methods=['POST']) def init(product): """ - This function initializes a product to a given user, + This function initializes/logs a product to a given user, i.e it will create a session for users/product/plans if the user has provided a user identifier it will be used as her primary email. The understanding is that a product may have multiple plans under it but always a free one @param uid user's email (primary) @param pid plan identifier """ + # + # get product and plans + # uid = request.headers['uid'] plan_id = request.headers['pid'] if 'pid' in request.headers else None + # + # We should just pull the factory method and get a storage handler to handle the logs + # store = dict(CONFIG['couchdb']) store['dbname'] = product user = User(stripe=stripe,store=store,product=product) @@ -193,7 +246,7 @@ def status(app_name): @pre 'uid' in request.headers """ @app.route('/signup/') -def signup(product) : +def _signup(product) : apikey = CONFIG['stripe']['pub'].strip() # @@ -263,7 +316,10 @@ def is_customer (app_name): return render_template('bill.html',context=CONTEXT,apikey=apikey,app_name=app_name.replace('-',' '),plans=plans,total_amount=amount) if __name__ == '__main__' : + # + # setup mongodb session management (not sure why) app.debug = True ; app.secret_key = '360-8y-[0v@t10n]+kr81v17y' app.config['MAX_CONTENT_LENGTH'] = 1600 * 1024 * 1024 + app.run(port=PORT,threaded=True,host='0.0.0.0') diff --git a/src/api/static/assets/google/normal.png b/src/api/static/assets/google/normal.png new file mode 100644 index 0000000..b1327b4 Binary files /dev/null and b/src/api/static/assets/google/normal.png differ diff --git a/src/api/static/css/default.css b/src/api/static/css/default.css new file mode 100644 index 0000000..67eea59 --- /dev/null +++ b/src/api/static/css/default.css @@ -0,0 +1,218 @@ +.theme-raspberry{ + background: #00B4DB; /* fallback for old browsers */ + background: -webkit-linear-gradient(to bottom, #0083B0, #00B4DB); /* Chrome 10-25, Safari 5.1-6 */ + background: linear-gradient(to bottom, #0083B0, #00B4DB); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */ + color:#ffffff; + font-family:verdana; + font-weight:lighter; + text-transform:capitalize; + +} +.theme-dark-ocean { + background: #373B44; /* fallback for old browsers */ + background: -webkit-linear-gradient(to bottom, #4682B4, #d3d3d3); /* Chrome 10-25, Safari 5.1-6 */ + background: linear-gradient(to bottom, #4682B4, #d3d3d3); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */ + color:#f3f3f3; +} +.theme-gray { + background: #bdc3c7; /* fallback for old browsers */ + background: -webkit-linear-gradient(to bottom, #2c3e50, #bdc3c7); /* Chrome 10-25, Safari 5.1-6 */ + background: linear-gradient(to bottom, #2c3e50, #bdc3c7); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */ + color:#f3f3f3f3; +} +.theme-clouds { + background: #ECE9E6; /* fallback for old browsers */ + background: -webkit-linear-gradient(to bottom, #FFFFFF, #D3D3D3); /* Chrome 10-25, Safari 5.1-6 */ + background: linear-gradient(to bottom, #FFFFFF, #D3D3D3); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */ + +} + + +img {width:48px} +body{ + font-family:sans-serif; + font-size:14px; + + width:80%; + margin-left:10%; +} +.default {font-size:16px; + font-family: sans-serif; +} +.button { + width:auto; + padding:6px; + font-weight:bold; + border-radius:8px; + border:2px solid #4682b4; + background-color:#4682b4; + color:white; + cursor:pointer; + text-align:center; + +} +.button:hover { + background-color:#ffffff; + color:#4682b4; + +} + +.active { + font-weight:bold; + font-family:verdana; + cursor:pointer ; + + border:2px solid transparent; + padding:4px; +} +.active:hover { border-bottom:2px solid #4682B4} +.current-plan { + + border-color: #20B2AA #20B2AA transparent transparent; + border-style: solid; + border-width: 15px 15px 15px 15px; + height: 0px; + width: 0px; + +} +.bold { font-weight:bold} +.caption { + font-size:28px; font-weight:bold ; + font-family:sans-serif; + text-transform:capitalize; + + } +.medium-caption { + font-size:18px; + font-weight:lighter; + font-family:sans-serif ; + text-transform:capitalize; +} +.plan { + padding:4px; + margin:4px; + border-radius: 4px; + border:1px solid #CAD5E0 +} +.pricing { + display:grid; + grid-template-columns: repeat(auto-fit,minmax(250px, 1fr) ); + + /* align-content:center; */ +} +.pricing-frame { + display:grid; + + + height:70%; + align-content:center; +} +.footer { + bottom:10px; + position:fixed; + width: 80%; + padding:8spx; + text-align:center; + display:grid; + grid-template-columns: 10% auto 10%; + grid-gap: 2px; +} +.plan .title { + display:grid; + grid-template-columns: auto 32px; + grid-gap:2px; + font-size:24px; font-family:verdana; text-transform: capitalize; border-bottom:1px solid #CAD5E0; } +.plan .feature {display:grid; grid-template-columns: 60% 40%; grid-gap:2px; margin:2px;} +.plan .feature .label {text-transform: uppercase; grid-column:1; font-size:14px;} +.plan .feature .status {font-size:20px; text-align: right; font-size:14px;} +.border{border:1px solid #CAD5E0;} +.border-bottom{ border-bottom:1px solid #CAD5E0} +.border-right{ border-right:1px solid #CAD5E0} +.border-left{ border-left:1px solid #CAD5E0} +.border-top{ border-top:1px solid #CAD5E0} +.left { float:left} +.small { font-family:verdana; font-size:11px; font-weight:lighter; color:#000000;} +.hidden {display:none} +.width-50{width:50%; padding:4px;} +.width-25{width:25%; padding:4px} +.height-48{height:48px;} +.fa-times {color:maroon; font-size:18px;} +.theme-dark-ocean .fa-check {color:green; font-size:18px} + +.fa-check {color:#20B2AA; font-size:18px;} +table { border:1px solid #CAD5E0; + border-radius:8px; + -webkit-border-radius:8px; + padding:4px; + + margin:4px; +} +.action { + cursor:pointer; + padding:4px; + padding-left:10px; + padding-right:10px; + border-radius:4px; + -webkit-border-radius:4px; + -moz-border-radius:4px; + font-weight:bold; + color:#4682b4; + +} +.action:hover { background-color:#4682b4; color:white} +tr {font-family:sans-serif; font-size:14px;} +td {padding:4px; font-weight:lighter; padding:4px;} +.gradient{ + background-image: -ms-linear-gradient(top, #008080 -120%, #FFFFFF 50%, #005757 120%); + background-image: -moz-linear-gradient(top, #008080 -120%, #FFFFFF 50%, #005757 120%); + background-image: -o-linear-gradient(top, #008080 -120%, #FFFFFF 50%, #005757 120%); + background-image: -webkit-gradient(linear, left top, left bottom, color-stop(-120, #008080), color-stop(50, #FFFFFF), color-stop(120, #005757)); + background-image: -webkit-linear-gradient(top, #008080 -120%, #FFFFFF 50%, #005757 120%); + background-image: linear-gradient(to bottom, #008080 -120%, #FFFFFF 50%, #005757 120%); +} +@media only screen and (min-device-width: 320px){ + table {width:99%} + img {width:28px} + .medium-caption {font-size:14px} + body {font-size:12px;} + .default {font-size:12px} + .height-48{height:40px} + .width-50{width:47%; padding:4px;} + .width-25{width:24%; padding:4px} + .pricing {display:block;} + .plan .title { font-size:18px; font-family:verdana; text-transform: capitalize; border-bottom:1px solid #CAD5E0; } + .plan .feature {display:grid; grid-template-columns: 60% 40%; grid-gap:4px; margin:4px} + .plan .feature .label {font-size:12px; text-transform: uppercase; grid-column:1} + .plan .feature .status {font-size:16px; text-align: right; font-size:14px;} + .header{display:none} + .caption {font-size:20px} + +} +@media only screen and (min-device-width: 768px){ + body {font-size:12px} + .default {font-size:12px} + .header .logo{ + display:grid; + margin:4px; + grid-template-columns: 48px auto ; + grid-gap:2px; + align-items: center; + } + .header .logo img {width:32px} + .header {display:block;} + + table {width:70%; margin-left:20%} + .medium-caption {font-size:18px} + .width-50{width:50%; padding:4px;} + .width-25{width:25%; padding:4px} + .height-48{height:48px;} + .pricing { + display:grid; + grid-template-columns: repeat(auto-fit,minmax(250px, 1fr) ) + } + .plan .title { padding:4px; font-size:22px; font-family:verdana; text-transform: capitalize; border-bottom:1px solid #CAD5E0; } + .plan .feature {display:grid; grid-template-columns: 60% 40%; grid-gap:4px; margin:4px; padding:4px;} + .plan .feature .label {text-transform: uppercase; grid-column:1} + .plan .feature .status {font-size:20px; text-align: right; font-size:14px;} + +} \ No newline at end of file diff --git a/src/api/static/img/accounts/box.png b/src/api/static/img/accounts/box.png new file mode 100644 index 0000000..98bd4e7 Binary files /dev/null and b/src/api/static/img/accounts/box.png differ diff --git a/src/api/static/img/accounts/dropbox.png b/src/api/static/img/accounts/dropbox.png new file mode 100644 index 0000000..e8f05ae Binary files /dev/null and b/src/api/static/img/accounts/dropbox.png differ diff --git a/src/api/static/img/accounts/microsoft.png b/src/api/static/img/accounts/microsoft.png new file mode 100644 index 0000000..a9411e7 Binary files /dev/null and b/src/api/static/img/accounts/microsoft.png differ diff --git a/src/api/static/img/accounts/one-drive.png b/src/api/static/img/accounts/one-drive.png new file mode 100644 index 0000000..0859441 Binary files /dev/null and b/src/api/static/img/accounts/one-drive.png differ diff --git a/src/api/templates/plans.html b/src/api/templates/plans.html new file mode 100755 index 0000000..908503a --- /dev/null +++ b/src/api/templates/plans.html @@ -0,0 +1,123 @@ + + + + + + + + + + + + + +{{product.replace('-',' ')}} + + + +
+ +
+ +
+
+ +

{{label}}
+
{{description}}
+ +

+ +
+
+ + {% for item in plans%} +
+
+
{{ item.nickname }}
+ {% if item.id in active_plans %} +
+ {% else %} +
+ {% endif %} + {% if item.amount == 0%} +
+ Free +
+ + {%else%} +
+ $ {{ item.amount/100 }} + / {{item.interval[:]}} + +
+ {% endif %} +
+ +
+ {% for key,value in item.metadata['features'].iteritems() %} +
+
{{ key }}
+ {% if value == True %} +
+ {% elif value == False %} +
+ {%else %} +
{{value}}
+ {% endif %} +
+ + {% endfor %} +
+ +
+

+

+ Signup Now +
+

+ + +
+ + +
+ {% endfor %} + +
+ +
+ + + + diff --git a/src/api/templates/signup.html b/src/api/templates/signup.html new file mode 100644 index 0000000..6bf3635 --- /dev/null +++ b/src/api/templates/signup.html @@ -0,0 +1,141 @@ + + + + + + + + + + + + +Signup {{product.replace('-',' ')}} + + +

+
+ +
+
+ +
+ +
+
+
{{label}}
+
+ + {{plan.nickname}} Plan, + + {{plan.amount / 100}} / {{plan.interval}} +
+
+
+

+

+ + {% for key,value in plan.metadata.features.iteritems() %} +
+
+ {{key}} +
+
+ {% if value == 1%} + + {% elif value == 0%} + + {% else %} + {{ value}} + {% endif %} +
+
+ {% endfor%} +
+

+
+
+ +
Signup using a cloud account
+ + +

+

+ + + + + +
+

+
+ +
+ + \ No newline at end of file