diff --git a/.gitignore b/.gitignore index 4dfbb1c..16fbfcb 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ node_modules/* *app.db* bundle.js bundle.min.js -yarn.lock \ No newline at end of file +yarn.lock +TODO \ No newline at end of file diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..f091553 --- /dev/null +++ b/Pipfile @@ -0,0 +1,37 @@ +[[source]] +url = "https://pypi.python.org/simple" +verify_ssl = true +name = "pypi" + +[dev-packages] + +[packages] +alembic = "*" +certifi = "*" +chardet = "*" +click = "*" +Flask = "*" +Flask-Login = "*" +Flask-Migrate = "*" +Flask-Script = "*" +Flask-SQLAlchemy = "*" +Flask-WTF = "*" +gunicorn = "*" +idna = "*" +itsdangerous = "*" +Jinja2 = "*" +Mako = "*" +MarkupSafe = "*" +python-dateutil = "*" +python-editor = "*" +requests = "*" +six = "*" +SQLAlchemy = "*" +urllib3 = "*" +Werkzeug = "*" +WTForms = "*" +mysqlclient = "*" +psycopg2 = "*" + +[requires] +python_version = "2.7" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..7c81401 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,248 @@ +{ + "_meta": { + "hash": { + "sha256": "b054d3d6094f33352a1122621d0baf48c7e0fe526d39015979a2236455cd361d" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "2.7" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.python.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "alembic": { + "hashes": [ + "sha256:85bd3ea7633024e4930900bc64fb58f9742dedbc6ebb6ecf25be2ea9a3c1b32e" + ], + "index": "pypi", + "version": "==0.9.9" + }, + "certifi": { + "hashes": [ + "sha256:13e698f54293db9f89122b0581843a782ad0934a4fe0172d2a980ba77fc61bb7", + "sha256:9fa520c1bacfb634fa7af20a76bcbd3d5fb390481724c597da32c719a7dca4b0" + ], + "index": "pypi", + "version": "==2018.4.16" + }, + "chardet": { + "hashes": [ + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + ], + "index": "pypi", + "version": "==3.0.4" + }, + "click": { + "hashes": [ + "sha256:29f99fc6125fbc931b758dc053b3114e55c77a6e4c6c3a2674a2dc986016381d", + "sha256:f15516df478d5a56180fbf80e68f206010e6d160fc39fa508b65e035fd75130b" + ], + "index": "pypi", + "version": "==6.7" + }, + "flask": { + "hashes": [ + "sha256:0749df235e3ff61ac108f69ac178c9770caeaccad2509cb762ce1f65570a8856", + "sha256:49f44461237b69ecd901cc7ce66feea0319b9158743dd27a2899962ab214dac1" + ], + "index": "pypi", + "version": "==0.12.2" + }, + "flask-login": { + "hashes": [ + "sha256:c815c1ac7b3e35e2081685e389a665f2c74d7e077cb93cecabaea352da4752ec" + ], + "index": "pypi", + "version": "==0.4.1" + }, + "flask-migrate": { + "hashes": [ + "sha256:493f9b3795985b9b4915bf3b7d16946697f027b73545384e7d9e3a79f989d2fe", + "sha256:b709ca8642559c3c5a81a33ab10839fa052177accd5ba821047a99db635255ed" + ], + "index": "pypi", + "version": "==2.1.1" + }, + "flask-script": { + "hashes": [ + "sha256:6425963d91054cfcc185807141c7314a9c5ad46325911bd24dcb489bd0161c65" + ], + "index": "pypi", + "version": "==2.0.6" + }, + "flask-sqlalchemy": { + "hashes": [ + "sha256:3bc0fac969dd8c0ace01b32060f0c729565293302f0c4269beed154b46bec50b", + "sha256:5971b9852b5888655f11db634e87725a9031e170f37c0ce7851cf83497f56e53" + ], + "index": "pypi", + "version": "==2.3.2" + }, + "flask-wtf": { + "hashes": [ + "sha256:5d14d55cfd35f613d99ee7cba0fc3fbbe63ba02f544d349158c14ca15561cc36", + "sha256:d9a9e366b32dcbb98ef17228e76be15702cd2600675668bca23f63a7947fd5ac" + ], + "index": "pypi", + "version": "==0.14.2" + }, + "gunicorn": { + "hashes": [ + "sha256:75af03c99389535f218cc596c7de74df4763803f7b63eb09d77e92b3956b36c6", + "sha256:eee1169f0ca667be05db3351a0960765620dad53f53434262ff8901b68a1b622" + ], + "index": "pypi", + "version": "==19.7.1" + }, + "idna": { + "hashes": [ + "sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f", + "sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4" + ], + "index": "pypi", + "version": "==2.6" + }, + "itsdangerous": { + "hashes": [ + "sha256:cbb3fcf8d3e33df861709ecaf89d9e6629cff0a217bc2848f1b41cd30d360519" + ], + "index": "pypi", + "version": "==0.24" + }, + "jinja2": { + "hashes": [ + "sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd", + "sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4" + ], + "index": "pypi", + "version": "==2.10" + }, + "mako": { + "hashes": [ + "sha256:4e02fde57bd4abb5ec400181e4c314f56ac3e49ba4fb8b0d50bba18cb27d25ae" + ], + "index": "pypi", + "version": "==1.0.7" + }, + "markupsafe": { + "hashes": [ + "sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665" + ], + "index": "pypi", + "version": "==1.0" + }, + "mysqlclient": { + "hashes": [ + "sha256:1e85e48b167e2af3bb08f273fdbd1ad6401cbe75057fa6513f97387dc7b282dc", + "sha256:2d9ec33de39f4d9c64ad7322ede0521d85829ce36a76f9dd3d6ab76a9c8648e5", + "sha256:371df79d000af56b4e540b7ce2120d1c9afb04b751bfce25a1eb609c50fd10ff", + "sha256:b3b1a7e4468180afb79289b54069d9499242946a4cedf3928cbf6b2a13800016", + "sha256:d56e379c03efad746e84705cbb97401f60d1f98b05e11a27f2d9c2d043936974" + ], + "index": "pypi", + "version": "==1.3.12" + }, + "psycopg2": { + "hashes": [ + "sha256:027ae518d0e3b8fff41990e598bc7774c3d08a3a20e9ecc0b59fb2aaaf152f7f", + "sha256:092a80da1b052a181b6e6c765849c9b32d46c5dac3b81bf8c9b83e697f3cdbe8", + "sha256:0b9851e798bae024ed1a2a6377a8dab4b8a128a56ed406f572f9f06194e4b275", + "sha256:179c52eb870110a8c1b460c86d4f696d58510ea025602cd3f81453746fccb94f", + "sha256:19983b77ec1fc2a210092aa0333ee48811fd9fb5f194c6cd5b927ed409aea5f8", + "sha256:1d90379d01d0dc50ae9b40c863933d87ff82d51dd7d52cea5d1cb7019afd72cd", + "sha256:27467fd5af1dcc0a82d72927113b8f92da8f44b2efbdb8906bd76face95b596d", + "sha256:32702e3bd8bfe12b36226ba9846ed9e22336fc4bd710039d594b36bd432ae255", + "sha256:33f9e1032095e1436fa9ec424abcbd4c170da934fb70e391c5d78275d0307c75", + "sha256:36030ca7f4b4519ee4f52a74edc4ec73c75abfb6ea1d80ac7480953d1c0aa3c3", + "sha256:363fbbf4189722fc46779be1fad2597e2c40b3f577dc618f353a46391cf5d235", + "sha256:6f302c486132f8dd11f143e919e236ea4467d53bf18c451cac577e6988ecbd05", + "sha256:733166464598c239323142c071fa4c9b91c14359176e5ae7e202db6bcc1d2eb5", + "sha256:7cbc3b21ce2f681ca9ad2d8c0901090b23a30c955e980ebf1006d41f37068a95", + "sha256:888bba7841116e529f407f15c6d28fe3ef0760df8c45257442ec2f14f161c871", + "sha256:8966829cb0d21a08a3c5ac971a2eb67c3927ae27c247300a8476554cc0ce2ae8", + "sha256:8bf51191d60f6987482ef0cfe8511bbf4877a5aa7f313d7b488b53189cf26209", + "sha256:8eb94c0625c529215b53c08fb4e461546e2f3fc96a49c13d5474b5ad7aeab6cf", + "sha256:8ebba5314c609a05c6955e5773c7e0e57b8dd817e4f751f30de729be58fa5e78", + "sha256:932a4c101af007cb3132b1f8a9ffef23386acc53dad46536dc5ba43a3235ae02", + "sha256:ad75fe10bea19ad2188c5cb5fc4cdf53ee808d9b44578c94a3cd1e9fc2beb656", + "sha256:aeaba399254ca79c299d9fe6aa811d3c3eac61458dee10270de7f4e71c624998", + "sha256:b178e0923c93393e16646155794521e063ec17b7cc9f943f15b7d4b39776ea2c", + "sha256:b68e89bb086a9476fa85298caab43f92d0a6af135a5f433d1f6b6d82cafa7b55", + "sha256:d74cf9234ba76426add5e123449be08993a9b13ff434c6efa3a07caa305a619f", + "sha256:f3d3a88128f0c219bdc5b2d9ccd496517199660cea021c560a3252116df91cbd", + "sha256:fe6a7f87356116f5ea840c65b032af17deef0e1a5c34013a2962dd6f99b860dd" + ], + "index": "pypi", + "version": "==2.7.4" + }, + "python-dateutil": { + "hashes": [ + "sha256:3220490fb9741e2342e1cf29a503394fdac874bc39568288717ee67047ff29df", + "sha256:9d8074be4c993fbe4947878ce593052f71dac82932a677d49194d8ce9778002e" + ], + "index": "pypi", + "version": "==2.7.2" + }, + "python-editor": { + "hashes": [ + "sha256:a3c066acee22a1c94f63938341d4fb374e3fdd69366ed6603d7b24bed1efc565" + ], + "index": "pypi", + "version": "==1.0.3" + }, + "requests": { + "hashes": [ + "sha256:6a1b267aa90cac58ac3a765d067950e7dbbf75b1da07e895d1f594193a40a38b", + "sha256:9c443e7324ba5b85070c4a818ade28bfabedf16ea10206da1132edaa6dda237e" + ], + "index": "pypi", + "version": "==2.18.4" + }, + "six": { + "hashes": [ + "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", + "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" + ], + "index": "pypi", + "version": "==1.11.0" + }, + "sqlalchemy": { + "hashes": [ + "sha256:d6cda03b0187d6ed796ff70e87c9a7dce2c2c9650a7bc3c022cd331416853c31" + ], + "index": "pypi", + "version": "==1.2.7" + }, + "urllib3": { + "hashes": [ + "sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b", + "sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f" + ], + "index": "pypi", + "version": "==1.22" + }, + "werkzeug": { + "hashes": [ + "sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c", + "sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b" + ], + "index": "pypi", + "version": "==0.14.1" + }, + "wtforms": { + "hashes": [ + "sha256:ffdf10bd1fa565b8233380cb77a304cd36fd55c73023e91d4b803c96bc11d46f" + ], + "index": "pypi", + "version": "==2.1" + } + }, + "develop": {} +} diff --git a/README.md b/README.md index e041d76..b5b9479 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,6 @@ This is a strong Flask base/boilerplate focusing on simplicity, while still havi - [Mail](#setting-up-mail) - [Deploying](#deploying) - ### Features - Always updated, production ready, modern tooling @@ -26,15 +25,14 @@ This is a strong Flask base/boilerplate focusing on simplicity, while still havi #### Libraries - **Flask:** Flask-SQLAlchemy, Flask-WTF, Flask-Login, Alembic/Flask-Migrate -- **JS & Frontend:** Bootstrap 4, Gulp, Livereload, and Webpack +- **JS & Frontend:** Bootstrap 4 (beta 2), Gulp, Livereload, and Webpack ### Quick Setup ```shell # Python -virtualenv venv -source venv/bin/activate -pip install -r requirements/base.txt +pipenv install +pipenv shell # Database python manage.py db init @@ -57,7 +55,7 @@ npm run buildprod # minified, prod-ready source venv/bin/activate python run.py -# Livereload and SASS +# Livereload and SASS compilation gulp # To recompile bundle.js (if you change app.js), run one the following: @@ -78,20 +76,12 @@ This boilerplate is ready for MySQL and Postgres, but uses sqlite by default. If 1. Create your database 2. In `app/config.py` you should comment the sqlite config line (11) and uncomment either mysql (19-20) or postgres (23-24). Make sure that you change the database name and add passwords or anything else you need to the connect string! -3. Execute either `pip install -r requirements.txt/mysql.txt` or `pip install -r requirements.txt/postgres.txt` (while inside virtual environment) to install the libraries needed for each respective database. +3. Execute either `pip install -r requirements/mysql.txt` or `pip install -r requirements/postgres.txt` (while inside virtual environment) to install the libraries needed for each respective database. ### Setting up mail *Optional* -A lot of applications tend to use Flask-Mail and suggest to create a gmail address to send email with. While this works fine for small things, it isn't production-ready and Gmail doesn't exist to send transactional email (forgot password, confirm email, etc.). For this reason, my mail setup uses Sparkpost which has 100,000 emails per month for free. I am not sponsored or compensated in any way by Sparkpost. I have never exceeded this monthly limit, and the platform is rich and simple to use. It has reporting and other features that are great for real world applications. - -1. Create an account on sparkpost.com -2. In your local environment, set the `SPARKPOST_API_KEY`: - `export SPARKPOST_API_KEY='yourkeyhere'` -3. Update mail config variables in config.py -4. All set! - -If you use another transactional platform like Mandrill, it should be very simple to drop in here. Feel free to submit a PR with a new branch for any alternative email setups. +Removed for now. ### Deploying diff --git a/app/__init__.py b/app/__init__.py index 4012c77..3ea13da 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -9,7 +9,7 @@ # App app = Flask(__name__) -config = app.config.from_object('app.config') +config = app.config.from_object('app.settings') # Database db = SQLAlchemy(app) diff --git a/app/config.py b/app/settings.py similarity index 100% rename from app/config.py rename to app/settings.py diff --git a/app/views.py b/app/views.py index c9d7cfc..6612c5f 100644 --- a/app/views.py +++ b/app/views.py @@ -3,7 +3,7 @@ from flask import render_template, flash, redirect, session, url_for, request, g, abort, send_from_directory from flask_login import login_user, logout_user, current_user, login_required from .models import * -from .config import * +from .settings import * from .forms import * import requests, json, time, datetime diff --git a/package.json b/package.json index 169264c..6c88e07 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "gulp-sass": "^2.3.2" }, "dependencies": { - "bootstrap": "^4.0.0-beta.2", + "bootstrap": "^4.0.0", "jquery": "^3.1.1", "popper.js": "^1.12.6", "tether": "^1.3.7", diff --git a/requirements/base.txt b/requirements/base.txt deleted file mode 100644 index ae9475e..0000000 --- a/requirements/base.txt +++ /dev/null @@ -1,24 +0,0 @@ -alembic==0.9.6 -certifi==2017.7.27.1 -chardet==3.0.4 -click==6.7 -Flask==0.12.2 -Flask-Login==0.4.0 -Flask-Migrate==2.1.1 -Flask-Script==2.0.6 -Flask-SQLAlchemy==2.3.2 -Flask-WTF==0.14.2 -gunicorn==19.7.1 -idna==2.6 -itsdangerous==0.24 -Jinja2==2.9.6 -Mako==1.0.7 -MarkupSafe==1.0 -python-dateutil==2.6.1 -python-editor==1.0.3 -requests==2.18.4 -six==1.11.0 -SQLAlchemy==1.1.14 -urllib3==1.22 -Werkzeug==0.12.2 -WTForms==2.1 diff --git a/requirements/mysql.txt b/requirements/mysql.txt deleted file mode 100644 index 4d990ac..0000000 --- a/requirements/mysql.txt +++ /dev/null @@ -1,2 +0,0 @@ --r base.txt -mysqlclient==1.3.9 \ No newline at end of file diff --git a/requirements/postgres.txt b/requirements/postgres.txt deleted file mode 100644 index 6ab7636..0000000 --- a/requirements/postgres.txt +++ /dev/null @@ -1,2 +0,0 @@ --r base.txt -psycopg2==2.6.2 \ No newline at end of file