├── architecture.png ├── 2.django-basic-settings ├── uwsgi.conf ├── {PROJECT NAME}.ini ├── nginx.conf └── README.md ├── 5.utilities-wiki-and-snippets ├── use-sass-for-stylesheet.md ├── use-travis-ci.md ├── use-gulp-as-task-runner.md ├── use-docker-at-mac-os-x.md ├── deploy-aws-lambda-using-zappa.md ├── use-webpack-as-module-bundler.md ├── manage-package-with-npm.md ├── multi-browser-test-by-selenium-grid.md ├── meteor-wiki.md └── deploy-with-fabric.md ├── 3.django-code-snippets ├── use-redis-to-manage-in-memory-db.md ├── use-celery-to-run-task-asynchronously.md ├── use-pillow-to-process-image.md ├── use-firebase-as-realtime-db.md ├── upload-image-to-cloudinary.md ├── send-mail-with-template.md ├── internationalization.md └── detect-facebook-deauthorization.md ├── README.md ├── 1.ubuntu-basic-settings ├── .vimrc └── README.md └── 4.deploy-django-with-aws └── README.md /architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ukjin1192/web-stack-wiki-and-snippets/HEAD/architecture.png -------------------------------------------------------------------------------- /2.django-basic-settings/uwsgi.conf: -------------------------------------------------------------------------------- 1 | # Emperor uWSGI script 2 | 3 | description "uWSGI Emperor" 4 | start on runlevel [2345] 5 | stop on runlevel [06] 6 | 7 | exec uwsgi --master --die-on-term --emperor /etc/uwsgi/vassals/ 8 | -------------------------------------------------------------------------------- /5.utilities-wiki-and-snippets/use-sass-for-stylesheet.md: -------------------------------------------------------------------------------- 1 | #### Install SASS 2 | 3 | ~~~~ 4 | $ sudo apt-get install ruby-full 5 | $ sudo su -c "gem install sass" 6 | ~~~~ 7 | 8 | 9 | #### Convert SASS to CSS 10 | 11 | ~~~~ 12 | $ sass styles.scss:styles.css 13 | ~~~~ 14 | -------------------------------------------------------------------------------- /3.django-code-snippets/use-redis-to-manage-in-memory-db.md: -------------------------------------------------------------------------------- 1 | #### `views.py` 2 | 3 | ~~~~ 4 | from django.core.cache import cache 5 | from django_redis import get_redis_connection 6 | 7 | 8 | con = get_redis_connection('default') 9 | 10 | cache.set('foo', 'bar', timeout=10) # Create cache 11 | cache.get('foo') # Retrieve cache value 12 | con.expire(':1:foo', 100) # Extend cache TTL 13 | cache.delete('foo') # Delete cache 14 | ~~~~ 15 | -------------------------------------------------------------------------------- /5.utilities-wiki-and-snippets/use-travis-ci.md: -------------------------------------------------------------------------------- 1 | #### Sign up 2 | 3 | - Visit Travis CI 4 | - Sign up with Github account 5 | 6 | #### Add Travis CI to repository 7 | 8 | - Go to profile page 9 | - Flick the repository switch on 10 | - Add `.travis.yml` to the root of your repository 11 | - Language example here 12 | - Add `https://secure.travis-ci.org/{GITHUB USERNAME}/{REPOSITORY NAME}.png` to your `README.md` 13 | - `git push` and check at Github repository 14 | -------------------------------------------------------------------------------- /3.django-code-snippets/use-celery-to-run-task-asynchronously.md: -------------------------------------------------------------------------------- 1 | #### `cron.py` 2 | 3 | ~~~~ 4 | from celery import task 5 | 6 | 7 | @task() 8 | def sample_async_task(*args, **kwargs): 9 | return None 10 | ~~~~ 11 | 12 | 13 | #### `views.py` 14 | 15 | ~~~~ 16 | from cron import sample_async_task 17 | 18 | 19 | # Run task asynchronously with celery after 1 second 20 | sample_async_task.apply_async( 21 | args=[ 22 | 'foo_1', 23 | 'foo_2', 24 | ], 25 | kwargs={ 26 | 'foo_3': 'bar', 27 | 'foo_4': 'bar', 28 | }, 29 | countdown=1 30 | ) 31 | ~~~~ 32 | -------------------------------------------------------------------------------- /3.django-code-snippets/use-pillow-to-process-image.md: -------------------------------------------------------------------------------- 1 | #### `utilities.py` 2 | 3 | ~~~~ 4 | from PIL import ImageOps 5 | 6 | 7 | def process_image(image_file): 8 | """" 9 | Process image file 10 | """" 11 | file_extension = image_file.content_type.split('/')[1] 12 | 13 | if file_extension is not in ['png', 'jpeg', 'bmp', 'gif']: 14 | return None 15 | 16 | width, height = get_image_dimensions(raw_file) 17 | 18 | size = (100, 70) 19 | thumbnail_image = ImageOps.fit(image_file, size, ImageObj.ANTIALIAS) 20 | 21 | return None 22 | ~~~~ 23 | 24 | 25 | #### `views.py` 26 | 27 | ~~~~ 28 | from utilities import process_image 29 | 30 | 31 | for raw_file in request.FILES.getlist('file'): 32 | process_image(raw_file) 33 | ~~~~ 34 | -------------------------------------------------------------------------------- /2.django-basic-settings/{PROJECT NAME}.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | ## variables 3 | username = root 4 | projectname = {PROJECT NAME} 5 | projectpath = {PROEJECT PATH} 6 | 7 | ## config 8 | uid = www-data 9 | gid = www-data 10 | vhost = true 11 | # master 12 | master = true 13 | # maximum number of processes (usually, 2 * cores. More time consuming tasks, less processes(=workers) are required) 14 | processes = 2 15 | # respawn processes taking more than 20 seconds 16 | harakiri = 20 17 | # the socket (use the full path to be safe) 18 | socket = /dev/shm/%(projectname).sock 19 | # with appropriate permissions 20 | chmod-socket = 775 21 | enable-threads = true 22 | # Project path 23 | chdir = %(projectpath) 24 | # Django's wsgi file 25 | module = %(projectname).wsgi 26 | # clear environment on exit 27 | vacuum = true 28 | max-request = 5000 29 | daemonize = %(projectpath)/logs/uwsgi.log 30 | pidfile = %(projectpath)/logs/uwsgi.pid 31 | -------------------------------------------------------------------------------- /3.django-code-snippets/use-firebase-as-realtime-db.md: -------------------------------------------------------------------------------- 1 | #### Pre-settings at firebase 2 | 3 | - Make new repository at Firebase 4 | - Check `repository URL` and `API secret code` 5 | 6 | 7 | #### Install firebase 8 | 9 | ~~~~ 10 | $ pip install requests 11 | $ pip install python-firebase 12 | ~~~~ 13 | 14 | 15 | #### `settings.py` 16 | 17 | ~~~~ 18 | FIREBASE_USERNAME = '{USERNAME}' 19 | FIREBASE_REPO_URL = '{REPOSITORY URL}' 20 | FIREBASE_API_SECRET = '{API SECRET CODE}' 21 | ~~~~ 22 | 23 | 24 | #### `utilities.py` 25 | 26 | ~~~~ 27 | from firebase import firebase 28 | 29 | FIREBASE_USERNAME = getattr(settings, 'FIREBASE_USERNAME') 30 | FIREBASE_REPO_URL = getattr(settings, 'FIREBASE_REPO_URL') 31 | FIREBASE_API_SECRET = getattr(settings, 'FIREBASE_API_SECRET') 32 | 33 | authentication = firebase.FirebaseAuthentication(FIREBASE_API_SECRET, FIREBASE_USERNAME, True, True) 34 | firebase_obj = firebase.FirebaseApplication(FIREBASE_REPO_URL, authentication) 35 | 36 | 37 | def update_firebase_database(permalink, key, value): 38 | """ 39 | Update Firebase DB 40 | """ 41 | firebase_obj.put(permalink, key, value) 42 | 43 | return None 44 | ~~~~ 45 | 46 | 47 | #### `views.py` 48 | 49 | ~~~~ 50 | from utilities import update_firebase_database 51 | 52 | 53 | update_firebase_database( 54 | '/fpp/', 55 | 'bar', 56 | 0 57 | ) 58 | ~~~~ 59 | -------------------------------------------------------------------------------- /5.utilities-wiki-and-snippets/use-gulp-as-task-runner.md: -------------------------------------------------------------------------------- 1 | #### Install gulp 2 | 3 | ~~~~ 4 | $ cd {PROJECT PATH} 5 | # Remove old version 6 | $ npm rm gulp --global 7 | $ npm install gulp-cli --global 8 | $ npm init 9 | $ npm install gulp --save-dev 10 | ~~~~ 11 | 12 | 13 | #### Minify(Uglify) javascript files 14 | 15 | ~~~~ 16 | $ npm install gulp-uglify --save-dev 17 | $ vi gulpfile.js 18 | 19 | var gulp = require('gulp'); 20 | var uglify = require('gulp-uglify'); 21 | 22 | // Minify javascript files 23 | gulp.task('uglify', function () { 24 | return gulp.src('src/*.js') 25 | .pipe(uglify()) 26 | .pipe(gulp.dest('dist')); 27 | }); 28 | 29 | // Enroll watch task 30 | gulp.task('watch', function () { 31 | gulp.watch('src/*.js', ['uglify']); 32 | }); 33 | 34 | // Run watch task as default 35 | gulp.task('default', ['uglify', 'watch']); 36 | 37 | $ gulp 38 | ~~~~ 39 | 40 | 41 | #### Convert SASS to CSS 42 | 43 | ~~~~ 44 | $ npm install gulp-sass --save-dev 45 | $ vi gulpfile.js 46 | 47 | var gulp = require('gulp'); 48 | var sass = require('gulp-sass'); 49 | 50 | // Convert SASS to CSS 51 | gulp.task('styles', function() { 52 | gulp.src('sass/**/*.scss') 53 | .pipe(sass().on('error', sass.logError)) 54 | .pipe(gulp.dest('./css/')); 55 | }); 56 | 57 | // Enroll watch task 58 | gulp.task('watch', function () { 59 | gulp.watch('sass/**/*.scss', ['styles']); 60 | }); 61 | 62 | // Run watch task as default 63 | gulp.task('default', ['styles', 'watch']); 64 | 65 | $ gulp 66 | ~~~~ 67 | -------------------------------------------------------------------------------- /5.utilities-wiki-and-snippets/use-docker-at-mac-os-x.md: -------------------------------------------------------------------------------- 1 | #### Install docker 2 | 3 | - Download docker at this site 4 | - Docker will be installed with VirtualBox 5 | 6 | 7 | #### Docker command 8 | 9 | ~~~ 10 | # Search image 11 | $ docker search {IMAGE NAME} 12 | 13 | # Download image 14 | $ docker pull {IMAGE NAME} 15 | 16 | # List-up whole image 17 | $ docker images 18 | 19 | # Remove image 20 | $ docker rmi {IMAGE NAME} 21 | 22 | # Create container 23 | $ docker run -i -t -p {EXPOSING PORT} --name {CONTAINER NAME} {IMAGE NAME:TAG} {COMMAND(e.g. /bin/bash)} 24 | 25 | # List-up whole container 26 | $ docker ps -a 27 | 28 | # Rename container 29 | $ docker rename {ORIGINAL CONTAINER NAME} {NEW CONTAINER NAME} 30 | 31 | # Remove container 32 | $ docker rm {CONTAINER NAME} 33 | 34 | # Re-run stopped container 35 | $ docker start {CONTAINER NAME} 36 | 37 | # Restart container 38 | $ docker restart {CONTAINER NAME} 39 | 40 | # Attach container 41 | $ docker attach {CONTAINER NAME} 42 | 43 | # Stop container 44 | $ docker stop {CONTAINER NAME} 45 | 46 | # Create image 47 | $ vi Dockerfile [Fill out contents] 48 | $ docker build --tag {IMAGE NAME}:{TAG} 49 | 50 | # Create image from existing container 51 | $ docker commit {CONTAINER NAME} {NEW IMAGE NAME}:{TAG} 52 | 53 | # Check exposing port of container 54 | $ docker port {CONTAINER NAME} 55 | 56 | # Detach container 57 | (In container) Ctrl-P + Ctrl-Q 58 | 59 | # Start new shell on running container 60 | $ docker exec -it {CONTAINER NAME} /bin/bash 61 | ~~~~ 62 | -------------------------------------------------------------------------------- /3.django-code-snippets/upload-image-to-cloudinary.md: -------------------------------------------------------------------------------- 1 | #### Pre-settings at cloudinary 2 | 3 | - Enroll new application 4 | - Check API key and API secret code at Cloudinary 5 | 6 | 7 | #### Install cloudinary 8 | 9 | ~~~~ 10 | $ pip install cloudinary 11 | ~~~~ 12 | 13 | 14 | #### `settings.py` 15 | 16 | ~~~~~ 17 | CLOUDINARY_API_KEY = '576224373763765' 18 | CLOUDINARY_API_SECRET = 'aiVax8O_I2SfPe7ufT-Uy9GI7r4' 19 | ~~~~~ 20 | 21 | 22 | #### `utilities.py` 23 | 24 | ~~~~ 25 | import cloudinary 26 | import cloudinary.uploader 27 | 28 | CLOUDINARY_API_KEY = getattr(settings, 'CLOUDINARY_API_KEY') 29 | CLOUDINARY_API_SECRET = getattr(settings, 'CLOUDINARY_API_SECRET') 30 | 31 | cloudinary.config( 32 | cloud_name = '{YOUR APP NAME}', 33 | api_key = CLOUDINARY_API_KEY, 34 | api_secret = CLOUDINARY_API_SECRET 35 | ) 36 | 37 | 38 | def upload_image_to_cloudinary(image_obj): 39 | """ 40 | Updload image to Cloudinary and return media url 41 | """ 42 | # Check file type is image 43 | if image_obj.content_type.split('/')[0] != 'image': 44 | return '' 45 | 46 | file_size = image_obj._size 47 | width, height = get_image_dimensions(image_obj) 48 | 49 | cloudinary_obj = cloudinary.uploader.upload(image_obj, width=width, height=height) 50 | 51 | return cloudinary_obj['secure_url'] 52 | ~~~~ 53 | 54 | 55 | #### `views.py` 56 | 57 | ~~~~ 58 | from utilities import upload_image_to_cloudinary 59 | 60 | 61 | if 'image' in request.FILES: 62 | image_url = upload_image_to_cloudinary(request.FILES['image']) 63 | ~~~~ 64 | -------------------------------------------------------------------------------- /5.utilities-wiki-and-snippets/deploy-aws-lambda-using-zappa.md: -------------------------------------------------------------------------------- 1 | #### Install AWS CLI 2 | 3 | ~~~~ 4 | $ pip install awscli --upgrade 5 | ~~~~ 6 | 7 | #### Create IAM 8 | 9 | - http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html 10 | - Permission : `AdministratorAccess` 11 | 12 | #### Set AWS configuration at the local instance 13 | 14 | ~~~~ 15 | $ aws configure 16 | AWS Access Key ID [None]: {BLAH} 17 | AWS Secret Access Key [None]: {BLAH} 18 | Default region name [None]: ap-northeast-2 // SEOUL 19 | Default output format [None]: json 20 | ~~~~ 21 | 22 | - Check configuration 23 | 24 | ~~~~ 25 | $ cat ~/.aws/credentials 26 | $ cat ~/.aws/config 27 | ~~~~ 28 | 29 | #### Clone python project 30 | 31 | ~~~~ 32 | $ git clone {GIT REPOSITORY} 33 | $ cd {PROJECT DIRECTORY} 34 | ~~~~ 35 | 36 | #### Install virtualenv and install python packages 37 | 38 | ~~~~ 39 | $ pip install virtualenv --upgrade 40 | $ virtualenv {VIRTUALENV NAME} 41 | $ source {VIRTUALENV NAME}/bin/activate 42 | $ pip install -r pip-requirements.txt 43 | $ pip install zappa 44 | ~~~~ 45 | 46 | #### Deploy AWS lambda using Zappa 47 | 48 | ~~~~ 49 | $ zappa init 50 | What do you want to call this environment (default 'dev'): dev // Environment = Stage 51 | What do you want call your bucket? (default 'zappa-xi82x3v32'): 52 | Where is your app's function?: {PYTHON_MODULE.FUNCTION_NAME} 53 | Would you like to deploy this application globally? (default 'n') [y/n/(p)rimary]: n 54 | Does this look okay? (default 'y') [y/n]: y 55 | $ vi zappa_settings.json 56 | "keep_warm": false, 57 | $ zappa deploy {STAGE NAME} 58 | ~~~~ 59 | 60 | - After every code updates, 61 | 62 | ~~~~ 63 | $ zappa update {STAGE NAME} 64 | ~~~~ 65 | 66 | - Using local variables : https://github.com/Miserlou/Zappa#setting-environment-variables 67 | - Search logs at CloudWatch 68 | - Metric Filters 69 | - Expire Events After 70 | -------------------------------------------------------------------------------- /3.django-code-snippets/send-mail-with-template.md: -------------------------------------------------------------------------------- 1 | #### Edit security setting of account 2 | 3 | - Visit here 4 | - Click `Continue` 5 | - Visit here 6 | - Update to `Allow` 7 | 8 | #### `settings.py` 9 | 10 | ~~~~ 11 | EMAIL_HOST = 'smtp.gmail.com' 12 | EMAIL_HOST_USER = '{ADMIN GMAIL ACCOUNT}' 13 | EMAIL_HOST_PASSWORD = '{PASSWORD}' 14 | EMAIL_PORT = 587 15 | EMAIL_USE_TLS = True 16 | ~~~~ 17 | 18 | 19 | #### `utilities.py` 20 | 21 | ~~~~ 22 | from django.core.mail import EmailMultiAlternatives 23 | from django.template import Context 24 | from django.template.loader import get_template 25 | 26 | 27 | def send_mail_with_template(subject, template_name, user_from, *user_to, **dict_var): 28 | """ 29 | Send mail with template 30 | """ 31 | plaintext = get_template('email/email.txt') 32 | htmly = get_template(template_name) 33 | d = Context(dict_var) 34 | 35 | text_content = plaintext.render(d) 36 | html_content = htmly.render(d) 37 | 38 | msg = EmailMultiAlternatives( 39 | subject, 40 | text_content, 41 | user_from, 42 | user_to 43 | ) 44 | msg.attach_alternative(html_content, 'text/html') 45 | msg.send() 46 | 47 | return None 48 | ~~~~ 49 | 50 | 51 | #### `views.py` 52 | 53 | ~~~~ 54 | from utilities import send_mail_with_template 55 | 56 | 57 | send_mail_with_template( 58 | 'MAIL TITLE', 59 | 'email_content.html', 60 | '{ADMIN GMAIL ACCOUNT}', 61 | 'user_to_1@domain.com', 'user_to_2@domain.com', ..., 62 | name='Tim', year=2015 63 | ) 64 | ~~~~ 65 | 66 | #### `templates/email.txt` 67 | 68 | ~~~~ 69 | ANY TEXT HERE 70 | ~~~~ 71 | 72 | 73 | #### `templates/email_content.html` 74 | 75 | ~~~~ 76 |
77 | Hello, {{ name }}! 78 | You've got mail for year {{ year }}! 79 |
80 | ~~~~ 81 | -------------------------------------------------------------------------------- /5.utilities-wiki-and-snippets/use-webpack-as-module-bundler.md: -------------------------------------------------------------------------------- 1 | #### Install Webpack 2 | 3 | ~~~~ 4 | $ npm install webpack --global 5 | $ npm install webpack --save-dev 6 | ~~~~ 7 | 8 | 9 | #### Install packages for example and create modules 10 | 11 | ~~~~ 12 | $ npm install lodash jquery --save 13 | $ cd {PATH TO JS DIRECTORY} 14 | $ vi module-foo.js 15 | 16 | 'use strict'; 17 | 18 | var _ = require('lodash'); 19 | 20 | module.exports = function sumList() { 21 | return _.sum(arguments); 22 | }; 23 | 24 | $ vi module-bar.js 25 | 26 | 'use strict'; 27 | 28 | var $ = require('jquery'); 29 | 30 | module.exports = function helloWorld() { 31 | $('#hello-world').html('Hello world'); 32 | }; 33 | ~~~~ 34 | 35 | 36 | #### Create entry file and template 37 | 38 | ~~~~ 39 | $ vi index.js 40 | 41 | 'use strict' 42 | 43 | var foo = require('./foo'); 44 | var bar = require('./bar'); 45 | 46 | console.log(foo(1,2,3,4)); 47 | bar(); 48 | 49 | $ vi index.html 50 | 51 | 52 | 53 | 54 |
55 | 56 | 57 | 58 | 59 | ~~~~ 60 | 61 | 62 | #### Create configuration file and bundle it 63 | 64 | ~~~~ 65 | $ vi webpack.config.js 66 | 67 | 'use strict'; 68 | 69 | var webpack = require('webpack'); 70 | 71 | module.exports = { 72 | entry: { 73 | bundle: './index.js', 74 | vendor: ['jquery', 'lodash'], 75 | }, 76 | output: { 77 | path: './dist/', 78 | filename: '[name].js', 79 | }, 80 | plugins: [ 81 | new webpack.optimize.CommonsChunkPlugin( 82 | 'vendor', 83 | 'vendor.bundle.js' 84 | ) 85 | ], 86 | resolve: { 87 | extensions: ['', '.js', '.es6'] 88 | }, 89 | }; 90 | 91 | $ webpack 92 | ~~~~ 93 | -------------------------------------------------------------------------------- /3.django-code-snippets/internationalization.md: -------------------------------------------------------------------------------- 1 | #### Basic settings 2 | 3 | ~~~~ 4 | $ apt-get install gettext 5 | $ vi {PROJECT PATH}/{PROJECT NAME}/settings.py 6 | 7 | ... 8 | LANGUAGE_CODE = 'ko' 9 | ugettext = lambda s: s 10 | LANGUAGES = ( 11 | ('ko', ugettext('Korean')), 12 | ('en', ugettext('English')), 13 | ) 14 | LOCALE_PATHS = ( 15 | ROOT_DIR + '/locale/', # /var/www/mysite.com/locale 16 | ) 17 | USE_I18N = True 18 | USE_L10N = True 19 | ... 20 | MIDDLEWARE_CLASSES = ( 21 | 'django.contrib.sessions.middleware.SessionMiddleware', 22 | 'django.middleware.locale.LocaleMiddleware', 23 | 'django.middleware.common.CommonMiddleware', 24 | ... 25 | ) 26 | # TEMPLATE_CONTEXT_PROCESSORS have django.core.context_processors.i18n as default 27 | ~~~~ 28 | 29 | #### Internationalization in template code 30 | 31 | ~~~~ 32 | $ vi {PROJECT PATH}/{PROJECT NAME}/templates/test.html 33 | 34 | {% load i18n %} 35 | ... 36 | {% trans "Hello" %} 37 | ... 38 | 39 | $ cd {PROJECT PATH} 40 | $ django-admin.py makemessages -l {LOCALE NAME} 41 | ~~~~ 42 | 43 | 44 | #### Internationalization in javascript code 45 | 46 | ~~~~ 47 | $ vi {PROJECT PATH}/{PROJECT NAME}/urls.py 48 | 49 | from django.views.i18n import javascript_catalog 50 | 51 | js_info_dict = { 52 | 'packages': ('your.app.package',), 53 | } 54 | 55 | urlpatterns = [ 56 | url(r'^jsi18n/$', javascript_catalog, js_info_dict), 57 | ] 58 | 59 | $ vi {PROJECT PATH}/{PROJECT NAME}/templates/test.html 60 | 61 | 64 | 65 | $ cd {PROJECT PATH} 66 | $ django-admin.py makemessages -d djangojs -l {LOCALE NAME} 67 | ~~~~ 68 | 69 | 70 | #### Localization 71 | 72 | ~~~~ 73 | $ vi {PROJECT PATH}/locale/{LOCALE NAME}/LC_MESSAGES/django.po 74 | 75 | msgid "Hello" 76 | msgstr "안녕" 77 | 78 | $ cd {PROJECT PATH} 79 | $ django-admin.py compilemessages 80 | ~~~~ 81 | -------------------------------------------------------------------------------- /5.utilities-wiki-and-snippets/manage-package-with-npm.md: -------------------------------------------------------------------------------- 1 | #### Install npm 2 | 3 | - 16.04 (Install `nodejs-legacy` to use `node` command instead of `nodejs`) 4 | 5 | ~~~~ 6 | $ apt-get update 7 | # 8 | $ apt-get install nodejs npm nodejs-legacy 9 | ~~~~ 10 | 11 | - 18.04 12 | 13 | ~~~~ 14 | $ sudo apt install curl 15 | $ curl -sL https://deb.nodesource.com/setup_8.x | sudo bash - 16 | $ sudo apt install nodejs 17 | ~~~~ 18 | 19 | 20 | #### npm command 21 | 22 | ~~~~ 23 | # Install packages with package.json 24 | $ cd {PATH TO package.json} 25 | $ npm install 26 | 27 | # Install package globally 28 | $ npm install {MODULE NAME}@{VERSION} --global 29 | 30 | # Install package to run this application 31 | $ npm install {MODULE NAME}@{VERSION} --save 32 | 33 | # Install package for development purpose (e.g. unit test or minification) 34 | $ npm install {MODULE NAME}@{VERSION} --save-dev 35 | 36 | # Uninstall packaes - same with install command 37 | $ npm uninstall {MODULE NAME}@{VERSION} --global 38 | $ npm uninstall {MODULE NAME}@{VERSION} --save 39 | $ npm uninstall {MODULE NAME}@{VERSION} --save-dev 40 | 41 | # Check version of module 42 | $ npm list {MODULE NAME} 43 | 44 | # Check list of whole module 45 | $ npm ls --depth=0 46 | ~~~~ 47 | 48 | 49 | #### Package management 50 | 51 | - Initiate package management 52 | 53 | ~~~~ 54 | $ cd {PROJECT PATH} 55 | $ npm init [Put information] 56 | $ npm install --save lodash 57 | ~~~~ 58 | 59 | - Recommend to add `node_modules/` to `.gitignore` 60 | - Example of `package.json` 61 | 62 | ~~~~ 63 | { 64 | "name": "mysite.com", 65 | "version": "0.0.0", 66 | "description": "", 67 | "main": "index.js", 68 | "scripts": { 69 | "test": "echo \"Error: no test specified\" && exit 1" 70 | }, 71 | "author": "", 72 | "license": "BSD-2-Clause", 73 | "dependencies": { 74 | "lodash": "~3.10.1" 75 | } 76 | } 77 | ~~~~ 78 | 79 | 80 | #### Node style programming example 81 | 82 | ~~~~ 83 | $ vi index.js 84 | 85 | 'use strict'; 86 | 87 | var _ = require('lodash'); 88 | 89 | module.exports = function helloWorld() { 90 | _.times(10, function (index) { 91 | console.log('[' + index + '] hello world!'); 92 | }); 93 | }; 94 | 95 | $ vi test.js 96 | 97 | 'use strict'; 98 | 99 | var helloWorld = require('./'); 100 | helloWorld(); 101 | 102 | $ node test.js 103 | ~~~~ 104 | -------------------------------------------------------------------------------- /5.utilities-wiki-and-snippets/multi-browser-test-by-selenium-grid.md: -------------------------------------------------------------------------------- 1 | #### Get Selenium grid 2 | 3 | - Visit Selenium website 4 | - Download `Selenium Standalone Server` 5 | 6 | 7 | #### `SeleniumTest.java` 8 | 9 | ~~~~ 10 | import java.net.URL; 11 | import java.util.concurrent.TimeUnit; 12 | 13 | import org.openqa.selenium.By; 14 | import org.openqa.selenium.Dimension; 15 | import org.openqa.selenium.JavascriptExecutor; 16 | import org.openqa.selenium.Platform; 17 | import org.openqa.selenium.Point; 18 | import org.openqa.selenium.WebDriver; 19 | import org.openqa.selenium.remote.DesiredCapabilities; 20 | import org.openqa.selenium.remote.RemoteWebDriver; 21 | 22 | public class SeleniumTest { 23 | 24 | public static void main(String[] args) throws Exception { 25 | 26 | // Set web driver 27 | String baseURL = 'http://mysite.com'; 28 | String nodeURL = 'http://127.0.0.1:5566/wd/hub'; 29 | 30 | DesiredCapabilities capability = DesiredCapabilities.iphone(); 31 | capability.setBrowserName("firefox"); 32 | capability.setPlatform(Platform.VISTA); 33 | 34 | WebDriver driver = new RemoteWebDriver(new URL(nodeURL), capability); 35 | 36 | // Wait 3 seconds if element is not loaded yet 37 | driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS); 38 | 39 | // Set browser size 40 | driver.manage().window().setPosition(new Point(0, 0)); 41 | driver.manage().window().setSize(new Dimension(1024, 768)); 42 | 43 | // Enable native JS command 44 | JavascriptExecutor jse = (JavascriptExecutor) driver; 45 | 46 | // Test on target site 47 | driver.get(baseURL + '/'); 48 | jse.executeScript("$('#sample-modal').modal('hide');"); 49 | jse.executeScript("window.scrollTo(0,document.body.scrollHeight);"); 50 | driver.findElement(By.cssSelector('#login')).click(); 51 | driver.findElement(By.cssSelector('#search')).sendKeys('sample keyword'); 52 | driver.findElement(By.xpath("(//button[@type='submit'])[0]")).click(); 53 | driver.quit(); 54 | } 55 | } 56 | ~~~~ 57 | 58 | 59 | #### Run Selenium grid 60 | 61 | - Open terminal 62 | - Move to directory which have `Selenium Standalone Server` file 63 | - `java -jar selenium-server-standalone-2.xx.0.jar -role hub` 64 | - Check URL `localhost:4444/grid/console/` on browser 65 | - Open another terminal 66 | - `java -jar selenium-server-standalone-2.xx.0.jar -role webdriver http://127.0.0.1:4444/grid/register -port 5566` 67 | - Check URL `localhost:4444/grid/console/` on browser 68 | - Open `SeleniumTest.java` and run this code 69 | - Check URL `localhost:5566/wd/hub/` on browser *(Sessions)* 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Web stack architecture 2 | 3 | 4 | 5 | # Contents in this repository 6 | 7 | ## Ubuntu basic settings 8 | 9 | - Install basic packages 10 | - Set timezone 11 | - Customize **vim** editor 12 | - Install **zshell** and oh-my-zshell 13 | - Install **Fail2ban** to protect from malicious attack 14 | - Configure **git** setting 15 | - Install **nginx** *(Recommend version upper than 1.6)* 16 | - Install **MySQL** 17 | - Install **PostgreSQL** 18 | 19 | #### Mac OS X basic settings 20 | - Customize **terminal profile** 21 | - Optimize key input 22 | - Install **brew** 23 | 24 | 25 | ## Django basic settings 26 | 27 | - Install django and helpful packages 28 | - Clone sample django project if exist 29 | - Connect with **MySQL** 30 | - Connect with **PostgreSQL** 31 | - When **schema changed** 32 | - Use **django-suit** *(Custom admin interface)* 33 | - Use **django-compressor** *(Compress static files)* 34 | - Install **redis** and connect with django 35 | - Use **redisboard** at admin 36 | - Redis command 37 | - Install **celery** and connect with django 38 | - Celery command 39 | - Use **cerely beat** as cron task runner 40 | - Celery beat command 41 | - Install uWSGI and configure **Nginx and uWSGI settings** 42 | - Nginx command 43 | - uWSGI command 44 | - When number of **CPU** core or **memory** size changed 45 | - Configure **New Relic** settings 46 | - Install **Pillow** for image processing 47 | 48 | 49 | ## Django code snippets 50 | 51 | - **Internationalization** *(Localization)* 52 | - Send **mail** with template 53 | - Use **redis** to manage in-memory DB 54 | - Use **celery** to run task asynchronously 55 | - Use **pillow** to process image 56 | - Upload image to **cloudinary** 57 | - Detect **facebook** deauthorization 58 | - Use **firebase** as realtime DB 59 | 60 | 61 | ## Deploy django with Amazon Web Services 62 | 63 | - **EC2** *(OS: ubuntu 14.04 LTS)* 64 | - Use fixed IP with **Elastic IPs** 65 | - Load balance with **ELB**(Elastic Load Balancer) 66 | - Adapt SSL ceritificate at **ELB** 67 | - **Auto Scaling Groups** with **Cloud Watch** 68 | - Get access permission with **IAM**(Identity & Access Management) 69 | - **Route** 53 *(DNS)* 70 | - **RDS** *(MySQL)* 71 | - **ElastiCache** *(Redis)* 72 | - **S3** *(Storage)* 73 | - **CloudFront** *(CDN)* 74 | 75 | 76 | ## Utilities wiki and snippets 77 | 78 | - Deploy with **Fabric** 79 | - Manage package with **npm** 80 | - Use **Webpack** as module bundler 81 | - Use **Gulp** as task runner 82 | - Use **SASS** for stylesheet 83 | - Multi browser test by **Selenium grid** 84 | - Use **Docker** at Mac OS X 85 | - **MeteorJS** wiki 86 | -------------------------------------------------------------------------------- /5.utilities-wiki-and-snippets/meteor-wiki.md: -------------------------------------------------------------------------------- 1 | #### Install Meteor framework and MongoDB at once 2 | 3 | ~~~~ 4 | $ curl https://install.meteor.com/ | sh 5 | ~~~~ 6 | 7 | 8 | #### Create and build Meteor project 9 | 10 | ~~~~ 11 | $ meteor create {PROJECT NAME} 12 | $ meteor run 13 | ~~~~ 14 | 15 | 16 | #### Check basic CRUD methods 17 | 18 | - Run MongoDB console 19 | 20 | ~~~~ 21 | $ meteor mongo 22 | ~~~~ 23 | 24 | - MongoDB native command 25 | 26 | ~~~~ 27 | # Create 28 | mongo > db.tableName.insert( 29 | {fieldA: 1, fieldB: 'foo'} 30 | ); 31 | 32 | # Read 33 | mongo > db.tableName.find( 34 | {fieldA: 1}, 35 | {fieldA: 1, fieldB: 1} 36 | ); 37 | 38 | # Update 39 | mongo > db.tableName.update( 40 | {fieldA: 1}, 41 | {$set: {fieldB: 'foo changed'}}, 42 | {multi: true} 43 | ); 44 | 45 | # Delete 46 | mongo > db.tableName.remove( 47 | {fieldA: 1} 48 | ); 49 | ~~~~ 50 | 51 | - Meteor's Collection API 52 | 53 | ~~~~ 54 | # Declare Meteor collection 55 | TableName = new Meteor.Collection('tableName'); 56 | 57 | # Create 58 | TableName.insert( 59 | {fieldA: 1, fieldB: 'foo'} 60 | ); 61 | 62 | # Read 63 | TableName.find( 64 | {fieldA: 1}, 65 | {fields: {fieldA: 1, fieldB: 1}} 66 | ); 67 | 68 | # Update 69 | TableName.update( 70 | {fieldA: 1}, 71 | {$set: {fieldB: 'foo changed'}}, 72 | {multi: true} 73 | ); 74 | 75 | # Delete 76 | TableName.remove( 77 | {fieldA: 1} 78 | ); 79 | ~~~~ 80 | 81 | 82 | #### Install packages (Find more at atmosperejs.com) 83 | 84 | ~~~~ 85 | $ meteor add {PACKAGE NAME} 86 | ~~~~ 87 | 88 | 89 | #### Some helpful packages 90 | 91 | - accounts-password *(User account system)* 92 | - iron:router *(Router)* 93 | - aldeed:collection2 *(Automatic validation of insert and update)* 94 | - email *(Send email)* 95 | - twbs:bootstrap *(Bootstrap 3)* 96 | - rajit:bootstrap3-datepicker *(Bootstrap 3 datepicker)* 97 | - nemo64:bootstrap *(Configuration for Bootstrap 3)* 98 | - fourseven:scss *(SASS)* 99 | - meteorhacks:fast-render *(Render app before DDP connection alive)* 100 | - force-ssl *(Force to use SSL)* 101 | 102 | 103 | #### Remove some insecure packages 104 | 105 | ~~~~ 106 | $ meteor remove autopublish insecure 107 | ~~~~ 108 | 109 | 110 | #### Check installed package list 111 | 112 | ~~~~ 113 | $ meteor list 114 | ~~~~ 115 | 116 | 117 | #### Set directory structure 118 | 119 | - **client** *(Front-end source code like HTML, JS, CSS)* 120 | - **lib** *(Common code for both client and server)* 121 | - **private** *(Resources only accessible from the server side)* 122 | - **public** *(Accessible with root path ('/'). e.g. favicon.ico)* 123 | - **server** *(Back-end source code)* 124 | -------------------------------------------------------------------------------- /5.utilities-wiki-and-snippets/deploy-with-fabric.md: -------------------------------------------------------------------------------- 1 | #### Install `fabric` 2 | 3 | ~~~~ 4 | $ pip install fabric 5 | $ cd {PROJECT ROOT PATH} 6 | $ vi fabfile.py 7 | ~~~~ 8 | 9 | 10 | #### Basic of `fabfile.py` 11 | 12 | ~~~~ 13 | #!/bin/bash 14 | import os 15 | from fabric.api import * 16 | 17 | ROOT_DIR = os.path.dirname(__file__) 18 | PROJECT_NAME = {PROJECT NAME} 19 | 20 | # Deploy server information 21 | env.hosts = {HOST IP} 22 | env.user = 'ubuntu' 23 | env.key_filename = {PEM FILE PATH} 24 | env.port = 22 25 | 26 | 27 | def deploy(): 28 | """ 29 | Deploy at remote server 30 | """ 31 | with cd(ROOT_DIR): 32 | sudo("git pull origin master") 33 | sudo("ps -ef | grep uwsgi | grep -v grep | awk '{print $2}' | xargs kill -15") 34 | sudo("uwsgi --uid www-data --gid www-data --emperor /etc/uwsgi/vassals --master --die-on-term --daemonize=" + ROOT_DIR + "/logs/uwsgi.log") 35 | ~~~~ 36 | 37 | 38 | #### Install `boto` to integrate with Amazon Web Services 39 | 40 | ~~~~ 41 | $ pip install boto3 42 | ~~~~ 43 | 44 | - Edit `fabfile.py` 45 | 46 | ~~~~ 47 | #!/bin/bash 48 | import os 49 | from boto3.session import Session 50 | from fabric.api import * 51 | 52 | ROOT_DIR = os.path.dirname(__file__) 53 | PROJECT_NAME = {PROJECT NAME} 54 | 55 | AWS_ACCESS_KEY_ID = {AWS ACCESS KEY ID} 56 | AWS_SECRET_ACCESS_KEY = {AWS SECRET ACCESS KEY} 57 | 58 | 59 | # Deploy server information 60 | env.hosts = [] 61 | env.user = 'ubuntu' 62 | env.key_filename = {PEM FILE PATH} 63 | env.port = 22 64 | 65 | session = Session(aws_access_key_id=AWS_ACCESS_KEY_ID, 66 | aws_secret_access_key=AWS_SECRET_ACCESS_KEY, 67 | region_name='ap-northeast-2') # SEOUL REGION 68 | 69 | ec2 = session.resource('ec2') 70 | 71 | for instance in ec2.instances.all(): 72 | env.hosts.append(instance.public_dns_name) 73 | 74 | 75 | def deploy(): 76 | """ 77 | Deploy at remote server 78 | """ 79 | with cd(ROOT_DIR): 80 | sudo("git pull origin master") 81 | sudo("ps -ef | grep uwsgi | grep -v grep | awk '{print $2}' | xargs kill -15") 82 | sudo("uwsgi --uid www-data --gid www-data --emperor /etc/uwsgi/vassals --master --die-on-term --daemonize=" + ROOT_DIR + "/logs/uwsgi.log") 83 | ~~~~ 84 | 85 | 86 | #### Separate local command with remote command 87 | 88 | ~~~~ 89 | from fabric.operations import local as lrun, run 90 | from fabric.api import task 91 | from fabric.state import env 92 | 93 | @task 94 | def localhost(): 95 | env.run = lrun 96 | env.hosts = ['localhost'] 97 | 98 | @task 99 | def remote(): 100 | env.run = run 101 | env.hosts = ['some.remote.host'] 102 | 103 | @task 104 | def install(): 105 | env.run('deployment command') 106 | ~~~~ 107 | 108 | - Install on localhost : `fab localhost install` 109 | - Install on remote : `fab remote install` 110 | 111 | -------------------------------------------------------------------------------- /3.django-code-snippets/detect-facebook-deauthorization.md: -------------------------------------------------------------------------------- 1 | #### Pre-settings at facebook developers page 2 | 3 | - Facebook Developers > `My Apps` > `Settings` > `Advanced` 4 | - Put `Deauthorize Callback URL` (e.g. `https://mysite.com/facebook/deauthorize/`) 5 | 6 | 7 | #### `urls.py` 8 | 9 | ~~~~ 10 | urlpatterns += patterns( 11 | url( 12 | regex=r'^facebook/deauthorize/$', 13 | view='deautorize_facebook_logged_in_user' 14 | ), 15 | ) 16 | ~~~~ 17 | 18 | 19 | #### `settings.py` 20 | 21 | ~~~~ 22 | FACEBOOK_SECRET_CODE = {FACEBOOK SECRET CODE} 23 | ~~~~ 24 | 25 | 26 | #### `utilities.py` 27 | 28 | ~~~~ 29 | import base64 30 | import hashlib 31 | import hmac 32 | import json 33 | 34 | FACEBOOK_SECRET_CODE = getattr(settings, 'FACEBOOK_SECRET_CODE') 35 | 36 | 37 | def base64_url_decode(raw_url): 38 | """ 39 | Decode URL by base64 40 | """ 41 | padding_factor = (4 - len(raw_url) % 4) % 4 42 | raw_url += "="*padding_factor 43 | 44 | return base64.b64decode(unicode(raw_url).translate(dict(zip(map(ord, u'-_'), u'+/')))) 45 | 46 | 47 | def parse_facebook_signed_request(signed_request): 48 | """ 49 | Parse facebook signed request and recognize user ID 50 | """ 51 | temp = signed_request.split('.', 2) 52 | encoded_sig = temp[0] 53 | payload = temp[1] 54 | 55 | sig = base64_url_decode(encoded_sig) 56 | data = json.loads(base64_url_decode(payload)) 57 | 58 | # Unknown algorithm 59 | if data.get('algorithm').upper() != 'HMAC-SHA256': 60 | return None 61 | else: 62 | expected_sig = hmac.new(FACEBOOK_SECRET_CODE, msg=payload, digestmod=hashlib.sha256).digest() 63 | 64 | if sig != expected_sig: 65 | return None 66 | else: 67 | return data 68 | ~~~~ 69 | 70 | 71 | #### `views.py` 72 | 73 | ~~~~ 74 | from django.views.decorators.csrf import csrf_exempt 75 | from django.http import JsonResponse 76 | from django.views.decorators.http import require_http_methods 77 | from utilities import parse_facebook_signed_request 78 | 79 | @csrf_exempt 80 | @require_http_methods(['POST']) 81 | def deauthorize_facebook_logged_in_user(request): 82 | """ 83 | Deauthorize facebook logged in user 84 | """ 85 | if not 'signed_request' in request.POST: 86 | return JsonResponse({ 87 | 'state': 'fail', 88 | 'code': 1, 89 | 'message': 'Signed request parameter is required.' 90 | }) 91 | 92 | data = parse_facebook_signed_request(request.POST['signed_request']) 93 | 94 | if data is None: 95 | return JsonResponse({ 96 | 'state': 'fail', 97 | 'code': 2, 98 | 'message': 'Invalid signed request.' 99 | }) 100 | 101 | facebook_user_id = data['user_id'] 102 | 103 | return JsonResponse({ 104 | 'state': 'success', 105 | 'code': 1, 106 | 'message': 'Succeed to get deauthorized facebook user ID.' 107 | }) 108 | ~~~~ 109 | -------------------------------------------------------------------------------- /1.ubuntu-basic-settings/.vimrc: -------------------------------------------------------------------------------- 1 | " Pre-requirements 2 | " $ sudo apt-get install vim ctags cmake python-dev 3 | " Install Vundle 4 | " $ mkdir -p ~/.vim/bundle/ 5 | " $ cd ~/.vim/bundle/ 6 | " $ git clone https://github.com/gmarik/Vundle.vim.git ~/.vim/bundle/Vundle.vim 7 | " Install Plugins 8 | " $ vim [:PluginInstall] 9 | " Compile YouCompleteMe 10 | " $ cd ~/.vim/bundle/YouCompleteMe 11 | " $ ./install.sh 12 | set nocompatible 13 | filetype off 14 | set rtp+=~/.vim/bundle/Vundle.vim 15 | call vundle#begin() 16 | Plugin 'gmarik/Vundle.vim' " Manage Vundle Package 17 | Plugin 'Valloric/YouCompleteMe' " Auto complete 18 | Plugin 'majutsushi/tagbar' " Check function and global variable at side bar 19 | Plugin 'scrooloose/nerdtree' " Show directory tree at side bar 20 | Plugin 'scrooloose/syntastic' " Check Syntax error when save (includes PEP) 21 | Plugin 'scrooloose/nerdcommenter' " Annotate block (\cc) or delete block (\cu) 22 | Plugin 'nathanaelkane/vim-indent-guides' " Indent Guides 23 | Plugin 'altercation/vim-colors-solarized' " Color scheme 24 | Plugin 'bling/vim-airline' " Status bar 25 | Plugin 'kien/ctrlp.vim' " File finder and manage buffers 26 | call vundle#end() 27 | filetype plugin indent on 28 | 29 | " Use solarized color scheme 30 | syntax enable 31 | set background=dark 32 | if !has('gui_running') 33 | let g:solarized_termtrans=1 " Compatibility for Terminal 34 | if !(&t_Co >= 256 || $TERM == 'xterm-256color') 35 | set t_Co=16 36 | let g:solarized_termcolors=16 " Make Solarized use 16 colors for Terminal support 37 | endif 38 | endif 39 | colorscheme solarized 40 | 41 | " Indent Guides 42 | let g:indent_guides_enable_on_vim_startup=1 43 | let g:indent_guides_start_level=2 44 | let g:indent_guides_auto_colors=0 45 | autocmd VimEnter,Colorscheme * :hi IndentGuidesEven ctermbg=0 46 | 47 | " Default settings 48 | set autoindent " Auto indentation 49 | set smartindent " Smart indentation 50 | set tabstop=2 51 | set shiftwidth=2 52 | set softtabstop=2 53 | set hlsearch " Highlight search keywords 54 | set encoding=utf-8 " Encoding 55 | set fileencodings=utf-8,euckr 56 | set laststatus=2 " Show status bar always 57 | set splitright " Position vertically split window to right 58 | set backspace=indent,eol,start " allow backspacing over everything in insert mode 59 | 60 | " Filetype customization 61 | autocmd Filetype html setlocal tabstop=2 shiftwidth=2 expandtab 62 | autocmd Filetype ruby setlocal tabstop=2 shiftwidth=2 expandtab 63 | autocmd Filetype javascript setlocal tabstop=2 shiftwidth=2 softtabstop=0 expandtab 64 | autocmd Filetype python setlocal tabstop=8 shiftwidth=4 softtabstop=4 expandtab 65 | 66 | " CtrlP ignoring list 67 | let g:ctrlp_custom_ignore = { 68 | \ 'dir': '\v[\/](\.git|node_modules|\.sass-cache|bower_components|build|_site)$', 69 | \ 'file': '\v\.(exe|so|dll)$'} 70 | 71 | " Memorize cursor position 72 | au BufReadPost * 73 | \ if line("'\"") > 0 && line("'\"") <= line("$") | 74 | \ exe "norm g`\"" | 75 | \ endif 76 | 77 | " Vim Key Mapping 78 | nmap :tabprev 79 | nmap :tabnext 80 | nmap gg=G 81 | nmap :wq 82 | nmap :!reset 83 | nmap :TagbarToggle 84 | nmap :set paste! 85 | nmap :set paste 86 | nmap :CtrlP 87 | nmap :NERDTree 88 | -------------------------------------------------------------------------------- /2.django-basic-settings/nginx.conf: -------------------------------------------------------------------------------- 1 | user www-data; 2 | pid /run/nginx.pid; 3 | 4 | # Worker Settings (Maximum workers = worker_processes * worker_connections) 5 | worker_processes auto; # Usually number of CPU cores 6 | 7 | events { 8 | worker_connections 1024; # Usually memory size in Megabytes 9 | multi_accept on; # Needs enough number of workers 10 | use epoll; # Requires linux kernel version upper than 2.6 11 | } 12 | 13 | http { 14 | sendfile on; # Copy data between one FD and other from within the kernel 15 | tcp_nopush on; # Send its HTTP response head in one packet 16 | tcp_nodelay on; # Disable buffer data-sends (Good for sending frequent small bursts of data in real time) 17 | types_hash_max_size 2048; # Increase this value if types_hash error occurs 18 | 19 | include /etc/nginx/mime.types; 20 | default_type application/octet-stream; 21 | 22 | # Timeout Settings 23 | keepalive_timeout 15; 24 | client_body_timeout 10; # Send "request timed out" message to client if the body is not loaded in time 25 | client_header_timeout 10; # Send "request timed out" message to client if the body is not loaded in time 26 | send_timeout 10; # Delete client connection if the client stops reading data (Higher value, higher memory usage) 27 | 28 | # Logging Settings 29 | access_log /var/log/nginx/access.log; 30 | error_log /var/log/nginx/error.log; 31 | 32 | # Gzip Settings 33 | gzip on; # Enable compression 34 | gzip_disable "msie6"; # Prevent bug from IE 6 35 | gzip_vary on; # Return "Vary: Accept-Encoding" on header 36 | gzip_comp_level 6; # Higher value, more compression 37 | gzip_proxied expired no-cache no-store private auth; # Cases of gzip compression when request come through proxy 38 | gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; 39 | 40 | # Virtual Host Configs 41 | include /etc/nginx/conf.d/*.conf; 42 | include /etc/nginx/sites-enabled/*; 43 | 44 | # DOS Attack Protection 45 | limit_req_zone $binary_remote_addr zone=req_limit_per_ip:100m rate=20r/s; # Do not allow upper than 20 requests per second at an average 46 | limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:100m; 47 | 48 | # Connect to File Socket 49 | upstream {PROJECT NAME} { 50 | server unix:/dev/shm/{PROJECT NAME}.sock; 51 | } 52 | 53 | server { 54 | listen 80; 55 | server_name {DOMAIN OR PUBLIC IP}; # Place '_' for the development server 56 | charset utf-8; 57 | client_max_body_size 20M; 58 | 59 | # etags attribute requires version upper than 1.3 (default: on) 60 | 61 | # Logging Settings (Disable access log to boost up the speed) 62 | access_log {PROJECT PATH}/logs/nginx.access.log; 63 | error_log {PROJECT PATH}/logs/nginx.error.log; 64 | 65 | # Django static (Cache-control on HTTP header on client's browser) 66 | location /static { 67 | alias {PROJECT PATH}/static; 68 | expires 30d; 69 | } 70 | 71 | # Non-media requests to the Django server 72 | location / { 73 | # DOS Attack Protection 74 | limit_req zone=req_limit_per_ip burst=20 nodelay; # Bursts not exceeding 20 requests per second 75 | limit_conn conn_limit_per_ip 100; # Maximum 100 connections per IP 76 | 77 | uwsgi_pass {PROJECT NAME}; 78 | include /etc/nginx/uwsgi_params; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /1.ubuntu-basic-settings/README.md: -------------------------------------------------------------------------------- 1 | # Ubuntu basing settings 2 | 3 | #### Install basic packages 4 | 5 | ~~~~ 6 | $ apt-get update 7 | $ apt-get install build-essential git python-dev libxml2-dev libxslt1-dev libssl-dev libffi-dev libcurl4-openssl-dev python-pip curl sudo wget htop 8 | $ pip install --upgrade pip 9 | ~~~~ 10 | 11 | 12 | #### Set timezone 13 | 14 | ~~~~ 15 | $ dpkg-reconfigure tzdata [Select city] 16 | ~~~~ 17 | 18 | 19 | #### Customize vim editor 20 | 21 | ~~~~ 22 | $ sudo apt-get install vim ctags cmake 23 | $ vi ~/.vimrc 24 | ~~~~ 25 | 26 | - Copy and paste `.vimrc` 27 | 28 | ~~~~ 29 | $ mkdir -p ~/.vim/bundle 30 | $ cd ~/.vim/bundle 31 | $ git clone https://github.com/gmarik/Vundle.vim.git ~/.vim/bundle/Vundle.vim 32 | $ vi [Ignore errors][:PluginInstall] 33 | $ cd ~/.vim/bundle/YouCompleteMe 34 | $ ./install.py 35 | ~~~~ 36 | 37 | 38 | #### Install zshell and oh-my-zshell 39 | 40 | ~~~~ 41 | $ sudo apt-get install zsh 42 | $ chsh -s `which zsh` 43 | ~~~~ 44 | 45 | - Restart server 46 | - Check default shell (`echo $SHELL`) 47 | 48 | ~~~~ 49 | $ curl -L https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh | sh 50 | $ /bin/zsh 51 | ~~~~ 52 | 53 | - Append following codes 54 | 55 | ~~~~ 56 | $ vi ~/.zshrc 57 | 58 | setopt PROMPT_SUBST 59 | PROMPT='%(!.%F{red}.%F{cyan})%n%f@%F{yellow}%m%f%(!.%F{red}.)%} ➜ %{$(pwd|grep --color=always /)%${#PWD}G%} %{$fg_bold[blue]%}$(git_prompt_info)%{$fg_bold[blue]%} % %{$reset_color%}' 60 | 61 | $ source ~/.zshrc 62 | ~~~~ 63 | 64 | 65 | #### Install Fail2ban to protect from malicious attack 66 | 67 | ~~~~ 68 | $ sudo apt-get install fail2ban 69 | $ cd /etc/fail2ban 70 | $ cp jail.conf jail.local 71 | $ vi jail.local 72 | 73 | [DEFAULT] 74 | ignoreip = {SAFE IP} 75 | 76 | [ssh] 77 | enabled = true 78 | 79 | [ssh-ddos] 80 | enabled = true 81 | destemail = {EMAIL ADDRESS} 82 | 83 | $ sudo service fail2ban restart 84 | ~~~~ 85 | 86 | 87 | #### Configure git setting 88 | 89 | ~~~~ 90 | $ git config --global user.name {USERNAME} 91 | $ git config --global user.email {EMAIL ADDRESS} 92 | $ git config --global core.editor "vim" 93 | ~~~~ 94 | 95 | 96 | #### Install nginx *(Recommend version upper than 1.6)* 97 | 98 | - Only for ubuntu <= 14.04 99 | 100 | ~~~~ 101 | $ sudo apt-get install software-properties-common 102 | $ sudo add-apt-repository ppa:nginx/stable [Enter] 103 | ~~~~ 104 | 105 | - Then, 106 | 107 | ~~~~ 108 | $ sudo apt-get update 109 | $ sudo apt-get install nginx 110 | $ nginx -v [Check version] 111 | $ sudo service nginx start 112 | ~~~~ 113 | 114 | - Check URL `{PUBLIC IP}:80` on browser 115 | 116 | 117 | #### Install MySQL 118 | 119 | ~~~~ 120 | $ sudo apt-get update 121 | $ sudo apt-get install mysql-server mysql-client libmysqlclient-dev [Enter root password] 122 | $ sudo service mysql start 123 | ~~~~ 124 | 125 | 126 | #### Install PostgreSQL 127 | 128 | ~~~~ 129 | $ sudo apt-get update 130 | $ sudo apt-get install libpq-dev build-dep python-psycopg2 postgresql postgresql-contrib [Enter password] 131 | $ sudo service postgresql start 132 | ~~~~ 133 | 134 | 135 | # Mac OS X basic settings 136 | 137 | #### Customize terminal profile 138 | 139 | - Download solarized theme at here (e.g. `Solarized Dark.terminal`) 140 | - Open `Terminal` app 141 | - Open `Profile` by press key `Command` + `,` 142 | - `Profile` tab > Click gear icon > import > `Solarized Dark.terminal` > Click theme > Set default 143 | 144 | 145 | #### Optimize key input 146 | 147 | - Open `terminal` 148 | 149 | ~~~~ 150 | # Enable key repeat 151 | $ defaults write NSGlobalDomain ApplePressAndHoldEnabled -bool false 152 | ~~~~ 153 | 154 | - Download Karabiner from here 155 | - `Change Key` > `For PC Users` 156 | - Check `Use PC Style Home/End` and `Use PC Style PageUp/PageDown` 157 | 158 | 159 | #### Install brew 160 | 161 | - Open `terminal` 162 | 163 | ~~~~ 164 | $ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 165 | ~~~~ 166 | 167 | - Put password 168 | - Install some requirements 169 | 170 | - Install use useful libraries 171 | 172 | ~~~~ 173 | $ brew install python cmake zsh vim 174 | $ vi {Shell Configuration file} 175 | 176 | alias vi='/usr/local/bin/vim' 177 | alias vim='/usr/local/bin/vim' 178 | ~~~~ 179 | 180 | 181 | #### Install X-code command line developers tool 182 | 183 | ~~~~ 184 | $ xcode-select --install 185 | ~~~~ 186 | -------------------------------------------------------------------------------- /4.deploy-django-with-aws/README.md: -------------------------------------------------------------------------------- 1 | # Deploy django with Amazon Web Services 2 | 3 | - **EC2** *(OS: ubuntu 14.04 LTS)* 4 | - Use fixed IP with **Elastic IPs** 5 | - Load balance with **ELB**(Elastic Load Balancer) 6 | - Adapt SSL ceritificate at **ELB** 7 | - **Auto Scaling Groups** with **Cloud Watch** 8 | - Get access permission with **IAM**(Identity & Access Management) 9 | - **Route** 53 *(DNS)* 10 | - **RDS** *(MySQL)* 11 | - **ElastiCache** *(Redis)* 12 | - **S3** *(Storage)* 13 | - **CloudFront** *(CDN)* 14 | 15 | 16 | ## EC2 17 | 18 | - `EC2 menu` > `Launch Instance` > `Ubuntu Server 14.04 LTS` 19 | - Click `Next` until `6. Configure Security Group` 20 | 21 | > _If the application is CPU intensive, C level instance is more suitable than T level instance_ 22 | 23 | - Set inbound rule for security group as following 24 | 25 | Type | Protocol | Port | Source 26 | -----|----------|------|------- 27 | SSH | TCP | 22 | 0.0.0.0/0 (or Your IP) 28 | HTTP | TCP | 80 | 0.0.0.0/0 (for nginx) 29 | HTTPS | TCP | 443 | 0.0.0.0/0 (for nginx) 30 | Custom TCP Rule | TCP | 8000 | 0.0.0.0/0 (for test) 31 | 32 | - `Review and Launch` > `Launch` 33 | - Create PEM file and save it into local directory *(DO NOT share or delete PEM file)*- 34 | 35 | ~~~~ 36 | $ chmod 400 {PATH TO PEM FILE} 37 | ~~~~ 38 | 39 | 40 | #### Use fixed IP with Elastic IPs 41 | 42 | - `EC2 menu` > `Elastic IPs` > `Allocate New Address` > `Yes, Allocate` 43 | - Right click IP > `Release addresses` > `Yes, Release` > Select instance > `Release` 44 | - From now on, you can make SSH connect to instance with PEM file 45 | 46 | ~~~~ 47 | $ ssh -i {PATH TO PEM FILE} ubuntu@{EC2 PUBLIC DNS} 48 | 49 | OR, 50 | 51 | $ 52 | $ vi ~/.ssh/config 53 | 54 | Host * 55 | UseKeychain yes 56 | AddKeysToAgent yes 57 | TCPKeepAlive yes 58 | ServerAliveInterval 120 59 | 60 | host {NICKNAME} 61 | Hostname {EC2 PUBLIC DNS} 62 | User ubuntu 63 | IdentityFile {PATH TO PEM FILE} 64 | 65 | $ ssh-add {PEM FILE} 66 | $ ssh {NICKNAME} 67 | ~~~~ 68 | 69 | #### Load balance with ELB(Elastic Load Balancer) 70 | 71 | - Issue SSL certificate through *Certificate Manager* 72 | - `EC2 menu` > `Load Balancers` > `Create Load Balancer` > `Classic Load Balancer` 73 | - Put `Load Balancer name` > Click `Add` > Choose `HTTPS` > Click `Next` 74 | - Assign security group same as EC2 > Click `Next` 75 | - Choose exsiting certificate that we made before 76 | - Set `Ping Path` value `/health_check` for Health checks > Click `Next` 77 | - Choose EC2 instance 78 | - Edit `nginx.conf` 79 | 80 | ~~~~ 81 | http { 82 | 83 | server { 84 | 85 | listen 80; 86 | 87 | location /health_check { 88 | access_log off; 89 | return 200; 90 | } 91 | 92 | location / { 93 | 94 | if ($http_x_forwarded_proto != 'https') { 95 | return 301 https://$host$request_uri; 96 | } 97 | } 98 | } 99 | 100 | server { 101 | 102 | listen 443; 103 | 104 | location /health_check { 105 | access_log off; 106 | return 200; 107 | } 108 | 109 | location / { 110 | {COPY AND PASTE EXISTING CODE IN UPPER BLOCK WITH 80 PORT} 111 | } 112 | } 113 | } 114 | ~~~~ 115 | 116 | - Go to Route53 > Edit record set > Type : A record > Alias target : ELB that we made right before 117 | 118 | #### Adapt SSL certificate at ELB (Deprecated) 119 | 120 | - Recommend to use *AWS Certificate Manager* instead of following steps 121 | - Create private key 122 | 123 | ~~~~~ 124 | $ openssl req -new -newkey rsa:2048 -nodes -keyout mysite_com.key -out mysite_com.csr 125 | ~~~~~ 126 | 127 | - Purchase Comodo positive SSL certificate and put mysite_com.key to activate SSL 128 | - Check email from Comodo which contains below files 129 | - Root CA Certificate - `AddTrustExternalCARoot.crt` 130 | - Intermediate CA Certificate - `COMODORSAAddTrustCA.crt` 131 | - Intermediate CA Certificate - `COMODORSADomainValidationSecureServerCA.crt` 132 | - PositiveSSL Certificate - `mysite_com.crt` 133 | - Merge above files into one *(Be careful of the order)* 134 | 135 | ~~~~ 136 | $ cat mysite_com.crt COMODORSADomainValidationSecureServerCA.crt COMODORSAAddTrustCA.crt AddTrustExternalCARoot.crt > mysite_com_bundle.crt 137 | ~~~~ 138 | 139 | - `EC2 menu` > `Load Balancers` 140 | - Select load balancer and click `Listeners` tab 141 | - Click `Edit` and add listener as following 142 | 143 | Load Balancer Protocol | Load Balancer Port | Instance Protocol | Instance Port | Cipher | SSL Certificate 144 | -----------------------|--------------------|-------------------|---------------|--------|---------------- 145 | HTTPS | 443 | HTTP | 80 | Use default | Add 146 | 147 | - Click `Change` on `SSL Certificate` column and add certificate 148 | - Private key : `mysite_com.key` 149 | - Public key : `mysite_com_bundle.crt` 150 | - Certificate chain : `None` 151 | - Edit `nginx.conf` to redirect `HTTP` to `HTTPS` 152 | 153 | ~~~~ 154 | server { 155 | listen 80; 156 | ... 157 | location / { 158 | if ($http_x_forwarded_proto != 'https') { 159 | return 301 https://$host$request_uri; 160 | } 161 | ... 162 | } 163 | } 164 | ~~~~ 165 | 166 | #### Auto Scaling Groups with Cloud Watch 167 | 168 | - `EC2 menu` > `Instances` 169 | - Right click instance > `Image` > `Create Image` > Put `Image name` > `Create Image` 170 | - `EC2 menu` > `Launch Configurations` > `Create launch configuration` 171 | - `My AMIs` > Select AMI > Select instance type > Put `Name` > Check `Monitoring` > Click `Advanced Details` 172 | - Fill out `User data` as initial script when instance created. For example, 173 | 174 | ~~~~ 175 | #!/bin/bash 176 | cd {PROJECT PATH} 177 | git pull origin master (OR git clone {REMOTE URL} if pull request requires username and password) 178 | npm install 179 | cd {PATH TO PIP REQUIRMENTS FILE} 180 | pip install -r requirements.txt 181 | cd {PROJECT PATH} 182 | sudo service nginx restart 183 | fab update_staticfiles 184 | fab run_uwsgi 185 | fab run_celery 186 | ~~~~ 187 | 188 | - `Next: Add Storage` > `Next: Configure Security Group` 189 | - `Select an existing security group` > Select same one with EC2 > `Review` > `Create launch configuration` 190 | - `Choose an existing key pair` > Select same one with EC2 > `Create launch configuration` 191 | - Put `Group name` > Click empty input of `Subnet` > Select all *(...northeast...)* 192 | - Click `Advanced Details` > Check `Load Balancing` > Click empty input of `Load Balancing` > Choose ELB 193 | - Check `Monitoring` > `Next: Configure scaling pollicies` 194 | - Select `Use scaling policies to adjust the capacity of this group` > Put minimum and maximum number of instances. For example, 195 | - Scale between `1` and `4` instances. 196 | - Fill out `Increase Group Size` 197 | - `Take the action` : `Add` `1` `instances` 198 | - `Add new alarm` > Uncheck `Send a notification to` > Whenever `Average` of `CPU Utilization` Is `>=` `80` Percent For at least `1` consecutive period(s) of `1 Minute` > `Create alarm` 199 | - Fill out `Decrease Group Size` 200 | - `Take the action` : `Remove` `1` `instances` 201 | - `Add new alarm` > Uncheck `Send a notification to` > Whenever `Average` of `CPU Utilization` Is `<=` `10` Percent For at least `1` consecutive period(s) of `1 Minute` > `Create alarm` 202 | - `Next: Configure Notifications` > `Add notification` > `create topic` > Put `topic name` and `email address` 203 | - `Review` > `Create Auto Scaling Group` 204 | - `EC2 menu` > `Instances` 205 | - Check new instance is initializing and terminate original instance 206 | 207 | #### Get access permission with IAM(Identity & Access Management) 208 | 209 | - `IAM` > `Users` > `Create New Users` > Put `User Name` > Click `Show User Security Credentials` > Check `Access Key ID` and `Secret Access Key` > `Close` 210 | - Click newly created user > `Permissions` > `Attach Policy` > Select `AmazonEC2FullAccess` > `Attach Policy` 211 | - Get "how to access AWS instances programmatically" at here 212 | 213 | 214 | ## Route 53 215 | 216 | - If you don't have domain yet, get domain from `Route 53 menu` > `Domain Registration` 217 | - `Route 53 menu` > `DNS Management` > `Create Hosted Zone` 218 | - Put `Domain Name` 219 | - Click `Create Record Set` 220 | - Put values as following 221 | 222 | Type | Alias | Alias Target or Value 223 | -----|-------|---------------------- 224 | NS | No | {NAME SERVER LIST} 225 | A | Yes | {ELB A RECORD} 226 | MX | No | {MAIL SERVER} 227 | 228 | 229 | ## RDS 230 | 231 | - `RDS menu` > `Launch a DB Instance` > `MySQL` 232 | - Click `No` for `Multi-AZ deployment` option to use free-tier 233 | - Select `db.t2.micro` for `DB Instance Class` and `No` for `Multi-AZ Deployment` 234 | 235 | > _If the application mainly using ORM, high memory size would be required_ 236 | 237 | - Put `DB Instance Identifier`, `Master Username`, `Master Password` 238 | - `Launch DB Instance` 239 | - Edit inbound rule of security group as following 240 | 241 | Type | Protocol | Port | Source 242 | -----|----------|------|------- 243 | MySQL | TCP | 3306 | 0.0.0.0/0 244 | 245 | - Create database with MySQL command 246 | 247 | ~~~~ 248 | $ sudo su 249 | $ mysql -u {MASTER USERNAME} -p -h {RDS END POINT} 250 | $ {MASTER PASSWORD} 251 | mysql $ CREATE DATABASE {DB NAME} DEFAULT CHARACTER SET utf8; 252 | mysql $ exit; 253 | ~~~~ 254 | 255 | - Edit `settings.py` 256 | 257 | ~~~~ 258 | DATABASES = { 259 | 'default': { 260 | 'ENGINE': 'django.db.backends.mysql', 261 | 'NAME': '{DB NAME}', 262 | 'USER': '{MASTER USERNAME}', 263 | 'PASSWORD': '{MASTER PASSWORD}', 264 | 'HOST': '{RDS END POINT}', 265 | 'PORT': '3306', 266 | 'DEFAULT-CHARACTER-SET': 'utf8', 267 | } 268 | } 269 | ~~~~ 270 | 271 | - Migrate database 272 | 273 | ~~~~ 274 | $ cd {PROJECT PATH} 275 | $ ./manage.py migrate 276 | $ ./manage.py makemigrations {APPLICATION NAME} 277 | $ ./manage.py migrate {APPLICATION NAME} 278 | ~~~~ 279 | 280 | - `RDS` > `Event Subscriptions` > `Create Event Subscription` 281 | - Put name and create topic > Select `source type` as `Instances` > Select instace > `Create` 282 | 283 | 284 | ## ElastiCache 285 | 286 | - `ElastiCache menu` > `Launch Cache Cluster` > `Redis` 287 | - Uncheck `Enable Replication` option to use free-tier 288 | - Put `Cluster Name` and select `cache.t2.micro` for `Node Type` 289 | - Create new security group as following inbound rule 290 | 291 | Type | Protocol | Port | Source 292 | -----|----------|------|------- 293 | Custom TCP Rule | TCP | 6379 | 0.0.0.0/0 294 | 295 | - Edit `settings.py` 296 | 297 | ~~~~ 298 | CACHES = { 299 | 'default': { 300 | 'BACKEND': 'django_redis.cache.RedisCache', 301 | 'LOCATION': 'redis://{END POINT}:6379', 302 | 'OPTIONS': { 303 | 'CLIENT_CLASS': 'django_redis.client.DefaultClient', 304 | } 305 | } 306 | } 307 | ~~~~ 308 | 309 | 310 | ## S3 311 | 312 | - `S3 menu` > `Create Bucket` 313 | - Put `Bucket Name` and `Create` 314 | - `Set properties` > `Set permissions` > `Next` > `Create` 315 | - Follow below steps ff you want to allow everyone to read files 316 | - `Permissions` > `Access Control List` > `Manage public permissions` > Everyone Group > Set `Read` for both `Object access` and `Permissions access` 317 | - `Permissions` > `Bucket Policy` > Copy and paste following snippet > `Save` 318 | 319 | ~~~~ 320 | { 321 | "Version": "2008-10-17", 322 | "Id": "Policy1403276359730", 323 | "Statement": [ 324 | { 325 | "Sid": "Stmt1403276358543", 326 | "Effect": "Allow", 327 | "Principal": { 328 | "AWS": "*" 329 | }, 330 | "Action": "s3:GetObject", 331 | "Resource": "arn:aws:s3:::{BUCKET NAME}/*" 332 | } 333 | ] 334 | } 335 | ~~~~ 336 | 337 | 338 | ## CloudFront 339 | 340 | - `CloudFront menu` > `Create distribution` > Choose `Web` 341 | - Click `Origin Domain Name` and choose `bucket` under `Amazon S3 Buckets` 342 | - `Create distribution` 343 | - You can get `sample.png` from `{CLOUDFRONT DOMAIN NAME}/{FOLDER NAME}/sample.png` 344 | - Note that `{BUCKET NAME}` is not included in URL 345 | - When you'd like to update cache, go to `invalidations` tab 346 | -------------------------------------------------------------------------------- /2.django-basic-settings/README.md: -------------------------------------------------------------------------------- 1 | # Django basic settings 2 | 3 | 4 | #### Clone sample django project if exist 5 | 6 | ~~~~ 7 | $ ssh-keygen 8 | $ cat ~/.ssh/id_rsa.pub 9 | ~~~~ 10 | 11 | - Add public key to Github or Bitbucket 12 | 13 | ~~~~ 14 | $ mkdir -p /var/www/ 15 | $ cd /var/www/ 16 | $ git clone {GIT REMOTE ORIGIN URL} 17 | ~~~~ 18 | 19 | - If you want to remove existing packages, 20 | 21 | ~~~~ 22 | $ pip freeze | xargs pip uninstall -y 23 | ~~~~ 24 | 25 | - Install pip requirements 26 | 27 | ~~~~ 28 | $ pip install -r {PATH TO PIP REQUIREMENTS}/requirements.txt 29 | ~~~~ 30 | 31 | 32 | #### Install django 33 | 34 | ~~~~ 35 | $ pip install django 36 | $ cd /var/www/ 37 | $ django-admin startproject {PROJECT NAME} 38 | $ cd {PROJECT PATH} 39 | $ ./manage.py runserver 0.0.0.0:8000 [Ignore migration error messages] 40 | ~~~~ 41 | 42 | - Check URL `{PUBLIC IP}:8000` on browser 43 | 44 | 45 | #### Connect with MySQL 46 | 47 | ~~~~ 48 | $ pip install MySQL-python 49 | $ sudo service mysql restart 50 | $ mysql -u root -p [Enter password] 51 | mysql $ CREATE DATABASE {DB NAME} DEFAULT CHARACTER SET utf8; 52 | mysql $ exit; 53 | $ vi {PROJECT PATH}/{PROJECT NAME}/settings.py 54 | 55 | DATABASES = { 56 | 'default': { 57 | 'ENGINE': 'django.db.backends.mysql', 58 | 'NAME': '{DB NAME}', 59 | 'USER': 'root', 60 | 'PASSWORD': '{MySQL ROOT PASSWORD}', 61 | 'HOST': '', 62 | 'PORT': '', 63 | 'DEFAULT-CHARACTER-SET': 'utf8', 64 | }, 65 | } 66 | 67 | $ cd {PROJECT PATH} 68 | $ ./manage.py migrate 69 | ~~~~ 70 | 71 | 72 | #### Connect with PostgreSQL 73 | 74 | ~~~~ 75 | $ pip install psycopg2 76 | $ sudo su - postgres 77 | postgresql $ createdb {DB NAME} 78 | postgresql $ createuser -P {USERNAME} [enter password] 79 | postgresql $ exit 80 | $ vi {PROJECT PATH}/{PROJECT NAME}/settings.py 81 | 82 | DATABASES = { 83 | 'default': { 84 | 'ENGINE': 'django.db.backends.postgresql_psycopg2', 85 | 'NAME': '{DB NAME}', 86 | 'USER': 'root', 87 | 'PASSWORD': '{PostgreSQL PASSWORD}', 88 | 'HOST': '127.0.0.1', 89 | 'PORT': '5432', 90 | } 91 | } 92 | 93 | $ cd {PROJECT PATH} 94 | $ ./manage.py migrate 95 | ~~~~ 96 | 97 | - Note: If you using custom user model, it would be better to migrate after fill out your user model in `models.py` 98 | 99 | 100 | #### When schema changed 101 | 102 | ~~~~ 103 | $ ./manage.py makemigrations {APP NAME} 104 | $ ./manage.py migrate {APP NAME} 105 | ~~~~ 106 | 107 | 108 | #### Use django-suit *(Custom admin interface)* 109 | 110 | ~~~~ 111 | $ pip install django-suit 112 | $ vi {PROJECT PATH}/{PROJECT NAME}/settings.py 113 | 114 | INSTALLED_APPS = ( 115 | # django-suit should come before 'django.contrib.admin' 116 | 'suit', 117 | 'django.contrib.admin', 118 | ... 119 | ) 120 | 121 | # Django < 1.9 122 | from django.conf.global_settings import TEMPLATE_CONTEXT_PROCESSORS 123 | 124 | TEMPLATE_CONTEXT_PROCESSORS += ( 125 | 'django.core.context_processors.request', 126 | ) 127 | ~~~~ 128 | 129 | #### Install Django extensions 130 | 131 | ~~~~ 132 | $ pip install django-extensions 133 | ~~~~ 134 | 135 | - Commands 136 | 137 | ~~~~ 138 | $ ./manage.py shell_plus 139 | ~~~~ 140 | 141 | 142 | #### Use django-compressor *(Compress static files)* 143 | 144 | ~~~~ 145 | $ pip install django-compressor 146 | $ vi {PROJECT PATH}/{PROJECT NAME}/settings.py 147 | 148 | INSTALLED_APPS += ( 149 | 'compressor', 150 | ) 151 | 152 | # Use compressor only for production mode 153 | COMPRESS_ENABLED = not DEBUG 154 | COMPRESS_URL = STATIC_URL 155 | COMPRESS_ROOT = STATIC_ROOT 156 | COMPRESS_OUTPUT_DIR = 'CACHE' 157 | STATICFILES_FINDERS += ( 158 | 'compressor.finders.CompressorFinder', 159 | ) 160 | 161 | $ vi {TEMPLATE FILE} 162 | 163 | {% load compress %} 164 | ... 165 | {% compress css %} 166 | ... 167 | {% endcompress %} 168 | ... 169 | {% compress js %} 170 | ... 171 | {% endcompress %} 172 | 173 | $ cd {PROJECT PATH} 174 | $ ./manage.py collectstatic --noinput 175 | ~~~~ 176 | 177 | 178 | #### Install redis and connect with django 179 | 180 | ~~~~ 181 | $ sudo apt-get install redis-server 182 | $ pip install redis django-redis 183 | $ vi {PROJECT PATH}/{PROJECT NAME}/settings.py 184 | 185 | CACHES = { 186 | 'default': { 187 | 'BACKEND': 'django_redis.cache.RedisCache', 188 | 'LOCATION': 'redis://127.0.0.1:6379/1', 189 | 'OPTIONS': { 190 | 'CLIENT_CLASS': 'django_redis.client.DefaultClient', 191 | } 192 | } 193 | } 194 | ~~~~ 195 | 196 | 197 | #### Use redisboard at admin (Only works with local redis server) 198 | 199 | ~~~~ 200 | $ pip install django-redisboard 201 | $ vi {PROJECT PATH}/{PROJECT NAME}/settings.py 202 | 203 | INSTALLED_APPS += ( 204 | 'redisboard', 205 | ) 206 | 207 | $ ./manage.py makemigrations redisboard 208 | $ ./manage.py migrate redisboard 209 | ~~~~ 210 | 211 | - If you using django-compressor 212 | 213 | ~~~~ 214 | $ ./manage.py collectstatic --noinput 215 | ~~~~ 216 | 217 | - Go to admin page and login 218 | - `Redisboard` > `Redis Servers` > `Add Redis Server` 219 | - Label : `localhost` / Hostname : `127.0.0.1` 220 | - Check cache list at `Tools` > `Inspect` 221 | 222 | 223 | #### Redis command 224 | 225 | ~~~~ 226 | $ service redis-server start # Run redis as daemon 227 | $ redis-cli save # Save redis 228 | $ redis-cli flushall # Flush redis DB 229 | $ redis-cli shutdown # Shutdown redis 230 | ~~~~ 231 | 232 | 233 | #### Install uWSGI and configure Nginx and uWSGI settings 234 | 235 | ~~~~ 236 | $ pip install uwsgi 237 | $ rm /etc/nginx/nginx.conf /etc/nginx/sites-enabled/default /etc/nginx/sites-available/default 238 | $ vi {PROJECT PATH}/conf/nginx/nginx.conf 239 | ~~~~ 240 | 241 | - Copy `nginx.conf` and customize `{{DOMAIN OR PUBLIC IP}}`, `{PROJECT NAME}` and `{PROJECT PATH}` 242 | 243 | ~~~~ 244 | $ chmod 775 {PROJECT PATH}/conf/nginx/nginx.conf 245 | $ ln -s {PROJECT PATH}/conf/nginx/nginx.conf /etc/nginx/nginx.conf 246 | ~~~~ 247 | 248 | - Copy `uwsgi.conf` and paste 249 | 250 | ~~~~ 251 | $ chmod 775 {PROJECT PATH}/conf/uwsgi/uwsgi.conf 252 | $ ln -s {PROJECT PATH}/conf/uwsgi/uwsgi.conf /etc/init/uwsgi.conf 253 | $ vi {PROJECT PATH}/conf/uwsgi/{PROJECT NAME}.ini 254 | ~~~~ 255 | 256 | - Copy `{PROJECT NAME}.ini` and customize `{PROJECT NAME}` and `{PROJECT PATH}` 257 | 258 | ~~~~ 259 | $ chmod 775 {PROJECT NAME}.ini 260 | $ mkdir -p /etc/uwsgi/vassals/ 261 | $ ln -s {PROJECT PATH}/conf/uwsgi/{PROJECT NAME}.ini /etc/uwsgi/vassals/ 262 | ~~~~ 263 | 264 | - Initiate log files 265 | 266 | ~~~~ 267 | $ mkdir -p {PROJECT PATH}/logs/ 268 | $ cd {PROJECT PATH}/logs/ 269 | $ touch uwsgi.log uwsgi.pid 270 | $ chown www-data.www-data uwsgi.log uwsgi.pid 271 | ~~~~ 272 | 273 | 274 | #### Nginx command 275 | 276 | ~~~~ 277 | $ service nginx status # Check nginx status 278 | $ service nginx start # Start nginx 279 | $ service nginx restart # Restart nginx 280 | $ service nginx stop # Stop nginx 281 | ~~~~ 282 | 283 | 284 | #### uWSGI command 285 | 286 | ~~~~ 287 | $ uwsgi --uid www-data --gid www-data --emperor /etc/uwsgi/vassals --master --die-on-term --daemonize={LOG FILE PATH} # Run uWSGI 288 | $ ps -ef | grep uwsgi | grep -v grep | awk "{print $2}" | xargs kill -15' # Stop uWSGI 289 | ~~~~ 290 | 291 | 292 | #### Install celery and connect with django 293 | 294 | - Note: We should install `django-celery 3.2.1` manually and integrate it with `celery 3.2.25` 295 | 296 | ~~~~ 297 | $ pip install celery==3.2.25 amqp 298 | $ wget https://github.com/celery/django-celery/archive/v3.2.1.tar.gz 299 | $ tar xvfz v3.2.1.tar.gz 300 | $ cd django-celery-3.2.1 301 | $ python setup.py install 302 | ~~~~ 303 | 304 | - Integrate celery with django 305 | 306 | ~~~~ 307 | $ apt-get install rabbitmq-server 308 | $ vi {PROJECT PATH}/{PROJECT NAME}/settings.py 309 | 310 | import djcelery 311 | 312 | INSTALLED_APPS += ( 313 | 'djcelery', 314 | ) 315 | 316 | djcelery.setup_loader() 317 | BROKER_URL = 'amqp://guest:guest@localhost:5672/' 318 | 319 | $ vi {PROJECT PATH}/{PROJECT NAME}/wsgi.py 320 | 321 | import djcelery 322 | 323 | djcelery.setup_loader() 324 | 325 | $ cd {PROJECT PATH} 326 | $ ./manage.py makemigrations djcelery 327 | $ ./manage.py migrate djcelery 328 | ~~~~ 329 | 330 | 331 | #### Celery command 332 | 333 | - Check rabbitmq is running. If it is not running, type 334 | 335 | ~~~~ 336 | $ service rabbitmq-server start 337 | ~~~~ 338 | 339 | - If you want to run celery with root permission, add following code at shell configuration file such as `~/.zshrc` 340 | 341 | ~~~~ 342 | $ vi {SHELL CONFIGURATION FILE} 343 | 344 | export C_FORCE_ROOT='true' 345 | 346 | $ source {SHELL CONFIGURATION FILE} 347 | ~~~~ 348 | 349 | ~~~~ 350 | $ cd {PROJECT PATH} 351 | $ ./manage.py celeryd_detach --logfile=logs/celery_daemon.log --pidfile=logs/celery_daemon.pid # Start celery as daemon 352 | $ ./manage.py celery worker --loglevel=debug # Start celery with debug mode 353 | $ ./manage.py celery purge # Flush celery tasks 354 | $ ps auxww | grep 'celery worker' | grep -v grep | awk '{print $2}' | xargs kill -15 # Stop celery 355 | ~~~~ 356 | 357 | 358 | #### Use cerely beat as cron task runner 359 | 360 | - Enroll tasks at Admin > Djcelery > Periodic tasks 361 | 362 | ~~~~ 363 | $ vi {PROJECT PATH}/{PROJECT NAME}/settings.py 364 | 365 | CELERY_IMPORTS = ('{Application}.{Python file that contains cron tasks}',) (e.g. 'utils.cron') 366 | CELERYBEAT_SCHEDULER = 'djcelery.schedulers.DatabaseScheduler' 367 | ~~~~ 368 | 369 | 370 | #### Celery beat command 371 | 372 | ~~~~ 373 | $ ./manage.py celery beat --logfile=logs/celery_beat.log --pidfile=logs/celery_beat.pid --detach # Start celery beat 374 | $ ps auxww | grep 'celery beat' | grep -v grep | awk '{print $2}' | xargs kill -15 # Stop celery beat 375 | ~~~~ 376 | 377 | 378 | #### Configure New Relic settings 379 | 380 | - Visit newrelic.com and login 381 | - `APM` > `Python application` 382 | - Get license key 383 | 384 | ~~~~ 385 | $ pip install newrelic 386 | $ mkdir -p {PROJECT PATH}/conf/newrelic/ 387 | $ cd {PROJECT PATH}/conf/newrelic/ 388 | $ newrelic-admin generate-config {LICENSE KEY} newrelic.ini 389 | $ vi {PROJECT PATH}/{PROJECT NAME}/wsgi.py 390 | 391 | import newrelic.agent 392 | 393 | newrelic.agent.initialize({PATH TO NEW RELIC CONFIGURATION}/newrelic.ini') 394 | application = newrelic.agent.wsgi_application()(application) 395 | ~~~~ 396 | 397 | - Restart uWSGI 398 | 399 | 400 | #### Install Pillow for image processing 401 | 402 | ~~~~ 403 | $ apt-get install imagemagick libjpeg-dev libfreetype6 libfreetype6-dev zlib1g-dev 404 | 405 | # 32bit : i386-linux-gnu, 64bit : x86_64-linux-gnu 406 | $ ln -s /usr/lib/x86_64-linux-gnu/libjpeg.so /usr/lib 407 | $ ln -s /usr/lib/x86_64-linux-gnu/libfreetype.so /usr/lib 408 | $ ln -s /usr/lib/x86_64-linux-gnu/libz.so /usr/lib 409 | 410 | # Only for ubuntu 14.04 LTS version 411 | $ ln -s /usr/include/freetype2 /usr/include/freetype 412 | 413 | $ pip install pillow 414 | ~~~~ 415 | 416 | - Check available list 417 | 418 | 419 | #### Install Django REST framework 420 | 421 | ~~~~ 422 | $ pip install djangorestframework 423 | $ pip install markdown 424 | $ pip install django-filter 425 | $ vi {PROJECT PATH}/{PROJECT NAME}/settings.py 426 | 427 | INSTALLED_APPS += ( 428 | 'rest_framework', 429 | ) 430 | ~~~~ 431 | 432 | 433 | #### Use django-silk *(live profiling and inspection tool)* 434 | 435 | ~~~~ 436 | $ pip install django-silk 437 | $ vi {PROJECT PATH}/{PROJECT NAME}/settings.py 438 | 439 | # Django >= 1.10 440 | MIDDLEWARE = ['silk.middleware.SilkyMiddleware', ] + MIDDLEWARE 441 | 442 | # Django <= 1.9 443 | MIDDLEWARE_CLASSES = ('silk.middleware.SilkyMiddleware', ) + MIDDLEWARE_CLASSES 444 | 445 | INSTALLED_APPS += ( 446 | 'silk', 447 | ) 448 | 449 | $ cd {PROJECT PATH} 450 | $ ./manage.py makemigrations silk 451 | $ ./manage.py migrate silk 452 | ~~~~ 453 | 454 | #### User JWT(Json Web Token) as authentication method in Django REST framework 455 | 456 | ~~~~ 457 | $ pip install djangorestframework-jwt 458 | $ vi {PROJECT PATH}/{PROJECT NAME}/settings.py 459 | 460 | REST_FRAMEWORK = { 461 | ... 462 | 'DEFAULT_AUTHENTICATION_CLASSES': ( 463 | ... 464 | 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', 465 | ... 466 | ) 467 | } 468 | 469 | $ {PROJECT PATH}/{PROJECT NAME}/urls.py 470 | 471 | from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token, verify_jwt_token 472 | 473 | urlpatterns = [ 474 | ... 475 | url( 476 | r'^api-token-auth/', 477 | obtain_jwt_token, 478 | ), 479 | url( 480 | r'^api-token-refresh/', 481 | refresh_jwt_token 482 | ), 483 | url( 484 | r'^api-token-verify/', 485 | verify_jwt_token 486 | ), 487 | ... 488 | ] 489 | ~~~~ 490 | 491 | - Usage 492 | - Get token 493 | - End-point: `{PROJECT DOMAIN}/api-token-auth/` 494 | - HTTP method: `POST` 495 | - parameters: `{'username': {USERNAME}, 'password': {PASSWORD}}` 496 | - return: `{'token': {TOKEN}}` 497 | - Verify token 498 | - End-point: `{PROJECT DOMAIN}/api-token-verify/` 499 | - HTTP method: `POST` 500 | - parameters: `{'token': {EXISTING TOKEN}}` 501 | - return: `200 OK` or `400 Bad Request` 502 | - Refresh token 503 | - End-point: `{PROJECT DOMAIN}/api-token-refresh/` 504 | - HTTP method: `POST` 505 | - parameters: `{'token': {EXISTING TOKEN}}` 506 | - return: `{'token': {NEW TOKEN}}` 507 | 508 | 509 | #### Use django-cors-headers to enable Cross-Origin-Resouce-Sharing 510 | 511 | ~~~~ 512 | $ pip install django-cors-headers 513 | $ vi {PROJECT PATH}/{PROJECT NAME}/settings.py 514 | 515 | INSTALLED_APPS = ( 516 | ... 517 | 'corsheaders', 518 | ... 519 | ) 520 | 521 | MIDDLEWARE = [ 522 | ... 523 | 'corsheaders.middleware.CorsMiddleware', 524 | 'django.middleware.common.CommonMiddleware', 525 | ... 526 | ] 527 | 528 | CORS_ORIGIN_WHITELIST = ( 529 | '*.mysite.com', 530 | ) 531 | --------------------------------------------------------------------------------