forked from gergely/calendar-social
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			test-cover
			...
			vagrant
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 60ad2c7ae2 | 
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -4,5 +4,5 @@ __pycache__/ | ||||
| /calsocial/translations/*/LC_MESSAGES/*.mo | ||||
| /.pytest_cache/ | ||||
| /.env | ||||
| /.coverage | ||||
| /htmlcov/ | ||||
| /.vagrant/ | ||||
| /ansible/*.retry | ||||
|   | ||||
							
								
								
									
										1
									
								
								Pipfile
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								Pipfile
									
									
									
									
									
								
							| @@ -18,7 +18,6 @@ flask-caching = "*" | ||||
| [dev-packages] | ||||
| pylint = "*" | ||||
| pytest = "*" | ||||
| pytest-cov = "*" | ||||
|  | ||||
| [requires] | ||||
| python_version = "3.6" | ||||
|   | ||||
							
								
								
									
										66
									
								
								Pipfile.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										66
									
								
								Pipfile.lock
									
									
									
										generated
									
									
									
								
							| @@ -1,7 +1,7 @@ | ||||
| { | ||||
|     "_meta": { | ||||
|         "hash": { | ||||
|             "sha256": "01a306fc25c75731af3fcf119a20d92c24fe5be9ddd8be2901b830df10bfb294" | ||||
|             "sha256": "e4313bc9baef5cb187176951d45094fe1de4ccba0d15ab58efbac21b6434f255" | ||||
|         }, | ||||
|         "pipfile-spec": 6, | ||||
|         "requires": { | ||||
| @@ -284,10 +284,10 @@ | ||||
|     "develop": { | ||||
|         "astroid": { | ||||
|             "hashes": [ | ||||
|                 "sha256:0a0c484279a5f08c9bcedd6fa9b42e378866a7dcc695206b92d59dc9f2d9760d", | ||||
|                 "sha256:218e36cf8d98a42f16214e8670819ce307fa707d1dcf7f9af84c7aede1febc7f" | ||||
|                 "sha256:8704779744963d56a2625ec2949eb150bd499fc099510161ddbb2b64e2d98138", | ||||
|                 "sha256:add3fd690e7c1fe92436d17be461feeaa173e6f33e0789734310334da0f30027" | ||||
|             ], | ||||
|             "version": "==2.0.1" | ||||
|             "version": "==2.0" | ||||
|         }, | ||||
|         "atomicwrites": { | ||||
|             "hashes": [ | ||||
| @@ -303,50 +303,6 @@ | ||||
|             ], | ||||
|             "version": "==18.1.0" | ||||
|         }, | ||||
|         "coverage": { | ||||
|             "hashes": [ | ||||
|                 "sha256:03481e81d558d30d230bc12999e3edffe392d244349a90f4ef9b88425fac74ba", | ||||
|                 "sha256:0b136648de27201056c1869a6c0d4e23f464750fd9a9ba9750b8336a244429ed", | ||||
|                 "sha256:104ab3934abaf5be871a583541e8829d6c19ce7bde2923b2751e0d3ca44db60a", | ||||
|                 "sha256:10a46017fef60e16694a30627319f38a2b9b52e90182dddb6e37dcdab0f4bf95", | ||||
|                 "sha256:15b111b6a0f46ee1a485414a52a7ad1d703bdf984e9ed3c288a4414d3871dcbd", | ||||
|                 "sha256:198626739a79b09fa0a2f06e083ffd12eb55449b5f8bfdbeed1df4910b2ca640", | ||||
|                 "sha256:1c383d2ef13ade2acc636556fd544dba6e14fa30755f26812f54300e401f98f2", | ||||
|                 "sha256:23d341cdd4a0371820eb2b0bd6b88f5003a7438bbedb33688cd33b8eae59affd", | ||||
|                 "sha256:28b2191e7283f4f3568962e373b47ef7f0392993bb6660d079c62bd50fe9d162", | ||||
|                 "sha256:2a5b73210bad5279ddb558d9a2bfedc7f4bf6ad7f3c988641d83c40293deaec1", | ||||
|                 "sha256:2eb564bbf7816a9d68dd3369a510be3327f1c618d2357fa6b1216994c2e3d508", | ||||
|                 "sha256:337ded681dd2ef9ca04ef5d93cfc87e52e09db2594c296b4a0a3662cb1b41249", | ||||
|                 "sha256:3a2184c6d797a125dca8367878d3b9a178b6fdd05fdc2d35d758c3006a1cd694", | ||||
|                 "sha256:3c79a6f7b95751cdebcd9037e4d06f8d5a9b60e4ed0cd231342aa8ad7124882a", | ||||
|                 "sha256:3d72c20bd105022d29b14a7d628462ebdc61de2f303322c0212a054352f3b287", | ||||
|                 "sha256:3eb42bf89a6be7deb64116dd1cc4b08171734d721e7a7e57ad64cc4ef29ed2f1", | ||||
|                 "sha256:4635a184d0bbe537aa185a34193898eee409332a8ccb27eea36f262566585000", | ||||
|                 "sha256:56e448f051a201c5ebbaa86a5efd0ca90d327204d8b059ab25ad0f35fbfd79f1", | ||||
|                 "sha256:5a13ea7911ff5e1796b6d5e4fbbf6952381a611209b736d48e675c2756f3f74e", | ||||
|                 "sha256:69bf008a06b76619d3c3f3b1983f5145c75a305a0fea513aca094cae5c40a8f5", | ||||
|                 "sha256:6bc583dc18d5979dc0f6cec26a8603129de0304d5ae1f17e57a12834e7235062", | ||||
|                 "sha256:701cd6093d63e6b8ad7009d8a92425428bc4d6e7ab8d75efbb665c806c1d79ba", | ||||
|                 "sha256:7608a3dd5d73cb06c531b8925e0ef8d3de31fed2544a7de6c63960a1e73ea4bc", | ||||
|                 "sha256:76ecd006d1d8f739430ec50cc872889af1f9c1b6b8f48e29941814b09b0fd3cc", | ||||
|                 "sha256:7aa36d2b844a3e4a4b356708d79fd2c260281a7390d678a10b91ca595ddc9e99", | ||||
|                 "sha256:7d3f553904b0c5c016d1dad058a7554c7ac4c91a789fca496e7d8347ad040653", | ||||
|                 "sha256:7e1fe19bd6dce69d9fd159d8e4a80a8f52101380d5d3a4d374b6d3eae0e5de9c", | ||||
|                 "sha256:8c3cb8c35ec4d9506979b4cf90ee9918bc2e49f84189d9bf5c36c0c1119c6558", | ||||
|                 "sha256:9d6dd10d49e01571bf6e147d3b505141ffc093a06756c60b053a859cb2128b1f", | ||||
|                 "sha256:9e112fcbe0148a6fa4f0a02e8d58e94470fc6cb82a5481618fea901699bf34c4", | ||||
|                 "sha256:ac4fef68da01116a5c117eba4dd46f2e06847a497de5ed1d64bb99a5fda1ef91", | ||||
|                 "sha256:b8815995e050764c8610dbc82641807d196927c3dbed207f0a079833ffcf588d", | ||||
|                 "sha256:be6cfcd8053d13f5f5eeb284aa8a814220c3da1b0078fa859011c7fffd86dab9", | ||||
|                 "sha256:c1bb572fab8208c400adaf06a8133ac0712179a334c09224fb11393e920abcdd", | ||||
|                 "sha256:de4418dadaa1c01d497e539210cb6baa015965526ff5afc078c57ca69160108d", | ||||
|                 "sha256:e05cb4d9aad6233d67e0541caa7e511fa4047ed7750ec2510d466e806e0255d6", | ||||
|                 "sha256:e4d96c07229f58cb686120f168276e434660e4358cc9cf3b0464210b04913e77", | ||||
|                 "sha256:f3f501f345f24383c0000395b26b726e46758b71393267aeae0bd36f8b3ade80", | ||||
|                 "sha256:f8a923a85cb099422ad5a2e345fe877bbc89a8a8b23235824a93488150e45f6e" | ||||
|             ], | ||||
|             "version": "==4.5.1" | ||||
|         }, | ||||
|         "isort": { | ||||
|             "hashes": [ | ||||
|                 "sha256:1153601da39a25b14ddc54955dbbacbb6b2d19135386699e2ad58517953b34af", | ||||
| @@ -421,11 +377,11 @@ | ||||
|         }, | ||||
|         "pylint": { | ||||
|             "hashes": [ | ||||
|                 "sha256:2c90a24bee8fae22ac98061c896e61f45c5b73c2e0511a4bf53f99ba56e90434", | ||||
|                 "sha256:454532779425098969b8f54ab0f056000b883909f69d05905ea114df886e3251" | ||||
|                 "sha256:248a7b19138b22e6390cba71adc0cb03ac6dd75a25d3544f03ea1728fa20e8f4", | ||||
|                 "sha256:9cd70527ef3b099543eeabeb5c80ff325d86b477aa2b3d49e264e12d12153bc8" | ||||
|             ], | ||||
|             "index": "pypi", | ||||
|             "version": "==2.0.1" | ||||
|             "version": "==2.0.0" | ||||
|         }, | ||||
|         "pytest": { | ||||
|             "hashes": [ | ||||
| @@ -435,14 +391,6 @@ | ||||
|             "index": "pypi", | ||||
|             "version": "==3.6.3" | ||||
|         }, | ||||
|         "pytest-cov": { | ||||
|             "hashes": [ | ||||
|                 "sha256:03aa752cf11db41d281ea1d807d954c4eda35cfa1b21d6971966cc041bbf6e2d", | ||||
|                 "sha256:890fe5565400902b0c78b5357004aab1c814115894f4f21370e2433256a3eeec" | ||||
|             ], | ||||
|             "index": "pypi", | ||||
|             "version": "==2.5.1" | ||||
|         }, | ||||
|         "six": { | ||||
|             "hashes": [ | ||||
|                 "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", | ||||
|   | ||||
							
								
								
									
										74
									
								
								Vagrantfile
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								Vagrantfile
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | ||||
| # -*- mode: ruby -*- | ||||
| # vi: set ft=ruby : | ||||
|  | ||||
| # All Vagrant configuration is done below. The "2" in Vagrant.configure | ||||
| # configures the configuration version (we support older styles for | ||||
| # backwards compatibility). Please don't change it unless you know what | ||||
| # you're doing. | ||||
| Vagrant.configure("2") do |config| | ||||
|   # The most common configuration options are documented and commented below. | ||||
|   # For a complete reference, please see the online documentation at | ||||
|   # https://docs.vagrantup.com. | ||||
|  | ||||
|   # Every Vagrant development environment requires a box. You can search for | ||||
|   # boxes at https://vagrantcloud.com/search. | ||||
|   config.vm.box = 'fedora/28-cloud-base' | ||||
|  | ||||
|   # Disable automatic box update checking. If you disable this, then | ||||
|   # boxes will only be checked for updates when the user runs | ||||
|   # `vagrant box outdated`. This is not recommended. | ||||
|   # config.vm.box_check_update = false | ||||
|  | ||||
|   # Create a forwarded port mapping which allows access to a specific port | ||||
|   # within the machine from a port on the host machine. In the example below, | ||||
|   # accessing "localhost:8080" will access port 80 on the guest machine. | ||||
|   # NOTE: This will enable public access to the opened port | ||||
|   config.vm.network "forwarded_port", guest: 80, host: 8080 | ||||
|  | ||||
|   # Create a forwarded port mapping which allows access to a specific port | ||||
|   # within the machine from a port on the host machine and only allow access | ||||
|   # via 127.0.0.1 to disable public access | ||||
|   # config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1" | ||||
|  | ||||
|   # Create a private network, which allows host-only access to the machine | ||||
|   # using a specific IP. | ||||
|   # config.vm.network "private_network", ip: "192.168.33.10" | ||||
|  | ||||
|   # Create a public network, which generally matched to bridged network. | ||||
|   # Bridged networks make the machine appear as another physical device on | ||||
|   # your network. | ||||
|   # config.vm.network "public_network" | ||||
|  | ||||
|   config.vm.synced_folder './', '/vagrant', type: 'sshfs' | ||||
|  | ||||
|   # Share an additional folder to the guest VM. The first argument is | ||||
|   # the path on the host to the actual folder. The second argument is | ||||
|   # the path on the guest to mount the folder. And the optional third | ||||
|   # argument is a set of non-required options. | ||||
|   # config.vm.synced_folder "../data", "/vagrant_data" | ||||
|  | ||||
|   # Provider-specific configuration so you can fine-tune various | ||||
|   # backing providers for Vagrant. These expose provider-specific options. | ||||
|   # Example for VirtualBox: | ||||
|   # | ||||
|   # config.vm.provider "virtualbox" do |vb| | ||||
|   #   # Display the VirtualBox GUI when booting the machine | ||||
|   #   vb.gui = true | ||||
|   # | ||||
|   #   # Customize the amount of memory on the VM: | ||||
|   #   vb.memory = "1024" | ||||
|   # end | ||||
|   # | ||||
|   # View the documentation for the provider you are using for more | ||||
|   # information on available options. | ||||
|  | ||||
|   # Enable provisioning with a shell script. Additional provisioners such as | ||||
|   # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the | ||||
|   # documentation for more information about their specific syntax and use. | ||||
|   config.vm.provision "ansible_local" do |ansible| | ||||
|     ansible.compatibility_mode = '2.0' | ||||
|     ansible.install = true | ||||
|     ansible.provisioning_path = '/vagrant/ansible' | ||||
|     ansible.playbook = 'dev.yml' | ||||
|   end | ||||
| end | ||||
							
								
								
									
										23
									
								
								ansible/dev.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								ansible/dev.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| --- | ||||
|  | ||||
| - name: Configuration for local development on Vagrant | ||||
|   hosts: all | ||||
|   become: yes | ||||
|   vars: | ||||
|     user_name: vagrant | ||||
|     group_name: vagrant | ||||
|  | ||||
|   roles: | ||||
|     - common | ||||
|     - python | ||||
|     - role: gunicorn | ||||
|       autostart: false | ||||
|       enabled: false | ||||
|     - role: nginx | ||||
|       use_ssl: false | ||||
|       enabled: false | ||||
|  | ||||
|   tasks: | ||||
|     - name: Allow virtualenv python to bind to port 80 | ||||
|       command: setcap cap_net_bind_service=ep /usr/bin/python3.6 | ||||
|       changed_when: false | ||||
							
								
								
									
										12
									
								
								ansible/install.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								ansible/install.sh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| #! /usr/bin/env bash | ||||
|  | ||||
| if [ ! -f /etc/ansible/hosts ] | ||||
| then | ||||
|     echo "Installing Ansible..." | ||||
|     sudo dnf remove ansible | ||||
|     sudo dnf install ansible-python3 | ||||
|  | ||||
|     printf 'localhost\n' | sudo tee /etc/ansible/hosts > /dev/null | ||||
| fi | ||||
|  | ||||
| echo "Ansible is installed." | ||||
							
								
								
									
										8
									
								
								ansible/roles/common/tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								ansible/roles/common/tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| --- | ||||
|  | ||||
| - name: Install required packages | ||||
|   dnf: | ||||
|     name: "{{ item }}" | ||||
|     state: present | ||||
|   with_items: | ||||
|     - libselinux-python | ||||
							
								
								
									
										10
									
								
								ansible/roles/common/vars/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								ansible/roles/common/vars/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| --- | ||||
|  | ||||
| # Project name | ||||
| project_name: calendar.social | ||||
|  | ||||
| # Project path | ||||
| project_path: /vagrant | ||||
|  | ||||
| # Flask app path | ||||
| application_path: /vagrant/app | ||||
							
								
								
									
										56
									
								
								ansible/roles/gunicorn/tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								ansible/roles/gunicorn/tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| --- | ||||
|  | ||||
| - name: Install Supervisor | ||||
|   dnf: | ||||
|     name: "{{ item }}" | ||||
|     state: present | ||||
|   with_items: | ||||
|     - supervisor | ||||
|  | ||||
| - name: Start supervisord | ||||
|   service: | ||||
|     name: supervisord | ||||
|     state: restarted | ||||
|  | ||||
| - name: Create the Gunicorn config directory | ||||
|   file: | ||||
|     path: /etc/gunicorn | ||||
|     state: directory | ||||
|     owner: "{{ user_name }}" | ||||
|     group: "{{ group_name }}" | ||||
|     mode: 0700 | ||||
|  | ||||
| - name: Create the Gunicorn config file in /etc/gunicorn | ||||
|   template: | ||||
|     src: gunicorn.conf.j2 | ||||
|     dest: /etc/gunicorn/gunicorn.conf | ||||
|  | ||||
| - name: Create the Gunicorn log directory | ||||
|   file: | ||||
|     path: /var/log/gunicorn | ||||
|     state: directory | ||||
|     owner: "{{ user_name }}" | ||||
|     group: "{{ group_name }}" | ||||
|     mode: 0700 | ||||
|  | ||||
| - name: Create the Supervisor config file for Gunicorn | ||||
|   template: | ||||
|     src: supervisor.conf.j2 | ||||
|     dest: /etc/supervisord.d/gunicorn.ini | ||||
|  | ||||
| - name: Re-read the Supervisor config files | ||||
|   supervisorctl: | ||||
|     name: gunicorn | ||||
|     state: present | ||||
|  | ||||
| - name: Start Gunicorn with supervisord | ||||
|   supervisorctl: | ||||
|     name: gunicorn | ||||
|     state: restarted | ||||
|   when: enabled | ||||
|  | ||||
| - name: Stop Gunicorn for local dev | ||||
|   supervisorctl: | ||||
|     name: gunicorn | ||||
|     state: stopped | ||||
|   when: not enabled | ||||
							
								
								
									
										9
									
								
								ansible/roles/gunicorn/templates/gunicorn.conf.j2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								ansible/roles/gunicorn/templates/gunicorn.conf.j2
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| import multiprocessing | ||||
|  | ||||
| workers = multiprocessing.cpu_count() * 2 + 1 | ||||
| proc_name = 'gunicorn' | ||||
| bind = '127.0.0.1:8000' | ||||
| errorlog = '/var/log/gunicorn/gunicorn-error.log' | ||||
| accesslog = '/var/log/gunicorn/gunicorn-access.log' | ||||
| loglevel = 'warning' | ||||
| timeout = 60 | ||||
							
								
								
									
										8
									
								
								ansible/roles/gunicorn/templates/supervisor.conf.j2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								ansible/roles/gunicorn/templates/supervisor.conf.j2
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| [program:gunicorn] | ||||
| command=pipenv run gunicorn wsgi:app -c /etc/gunicorn/gunicorn.conf --pythonpath {{ application_path }} | ||||
| directory={{ application_path }} | ||||
| user={{ user_name }} | ||||
| group={{ group_name }} | ||||
| autorestart=true | ||||
| autostart={{ autostart | bool | lower }} | ||||
| redirect_stderr=true | ||||
							
								
								
									
										11
									
								
								ansible/roles/nginx/handlers/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								ansible/roles/nginx/handlers/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| --- | ||||
|  | ||||
| - name: Reload Nginx | ||||
|   service: | ||||
|     name: nginx | ||||
|     state: reloaded | ||||
|  | ||||
| - name: Stop Nginx | ||||
|   service: | ||||
|     name: nginx | ||||
|     state: stopped | ||||
							
								
								
									
										42
									
								
								ansible/roles/nginx/tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								ansible/roles/nginx/tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| --- | ||||
|  | ||||
| - name: Install Nginx | ||||
|   dnf: | ||||
|     name: "{{ item }}" | ||||
|     state: present | ||||
|   with_items: | ||||
|     - nginx | ||||
|  | ||||
| - name: Create the Nginx configuration file for SSL | ||||
|   template: | ||||
|     src: site-ssl.conf.j2 | ||||
|     dest: /etc/nginx/conf.d/{{ project_name }}-ssl.conf | ||||
|   when: use_ssl | ||||
|   notify: Reload Nginx | ||||
|  | ||||
| - name: Create the Nginx configuration file (non-SSL) | ||||
|   template: | ||||
|     src: site.conf.j2 | ||||
|     dest: /etc/nginx/conf.d/{{ project_name }}.conf | ||||
|   when: not use_ssl | ||||
|   notify: Reload Nginx | ||||
|  | ||||
| - name: Ensure that the default site is removed | ||||
|   file: | ||||
|     path: /etc/nginx/conf.d/default.conf | ||||
|     state: absent | ||||
|  | ||||
| - name: Ensure Nginx service is started, enable service on restart | ||||
|   service: | ||||
|     name: nginx | ||||
|     state: restarted | ||||
|     enabled: yes | ||||
|   when: enabled | ||||
|  | ||||
| - name: Stop nginx for local dev, disable service | ||||
|   service: | ||||
|     name: nginx | ||||
|     state: stopped | ||||
|     enabled: no | ||||
|   notify: Stop Nginx | ||||
|   when: not enabled | ||||
							
								
								
									
										41
									
								
								ansible/roles/nginx/templates/site-ssl.conf.j2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								ansible/roles/nginx/templates/site-ssl.conf.j2
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| upstream appserver { | ||||
|   server localhost:8000 fail_timeout=0; | ||||
| } | ||||
|  | ||||
| server { | ||||
|   listen 80; | ||||
|   return 301 https://$host$request_uri; | ||||
| } | ||||
|  | ||||
| server { | ||||
|   listen 443 ssl deferred; | ||||
|   server_name {{ host_name }}; | ||||
|  | ||||
|   ssl_certificate {{ home_path }}/{{ project_name }}.crt; | ||||
|   ssl_certificate_key {{ home_path }}/{{ project_name }}.key; | ||||
|   ssl_session_cache shared:SSL:32m; | ||||
|   ssl_protocols TLSv1 TLSv1.1 TLSv1.2; | ||||
|   ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4; | ||||
|   ssl_prefer_server_ciphers on; | ||||
|  | ||||
|   access_log /var/log/nginx/{{ project_name }}.access.log; | ||||
|   error_log /var/log/nginx/{{ project_name }}.error.log info; | ||||
|  | ||||
|   keepalive_timeout 5; | ||||
|  | ||||
|   location /static { | ||||
|     alias {{ project_path }}/static; | ||||
|   } | ||||
|  | ||||
|   location / { | ||||
|     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | ||||
|     proxy_set_header Host $http_host; | ||||
|     proxy_redirect off; | ||||
|     proxy_read_timeout 180s; | ||||
|  | ||||
|     if (!-f $request_filename) { | ||||
|       proxy_pass http://appserver; | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										29
									
								
								ansible/roles/nginx/templates/site.conf.j2
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								ansible/roles/nginx/templates/site.conf.j2
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| upstream appserver { | ||||
|   server localhost:8000 fail_timeout=0; | ||||
| } | ||||
|  | ||||
| server { | ||||
|   listen 80; | ||||
|   server_name {{ host_name }}; | ||||
|  | ||||
|   access_log /var/log/nginx/{{ project_name }}.access.log; | ||||
|   error_log /var/log/nginx/{{ project_name }}.error.log info; | ||||
|  | ||||
|   keepalive_timeout 5; | ||||
|  | ||||
|   location /static { | ||||
|     alias {{ project_path }}/static; | ||||
|   } | ||||
|  | ||||
|   location / { | ||||
|     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | ||||
|     proxy_set_header Host $http_host; | ||||
|     proxy_redirect off; | ||||
|     proxy_read_timeout 180s; | ||||
|  | ||||
|     if (-f $request_filename) { | ||||
|       proxy_pass http://appserver; | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										3
									
								
								ansible/roles/nginx/vars/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								ansible/roles/nginx/vars/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| --- | ||||
|  | ||||
| host_name: calendar-social.local | ||||
							
								
								
									
										22
									
								
								ansible/roles/python/tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								ansible/roles/python/tasks/main.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| --- | ||||
|  | ||||
| - name: Install common python packages | ||||
|   dnf: | ||||
|     name: "{{ item }}" | ||||
|     state: present | ||||
|   with_items: | ||||
|     - pipenv | ||||
|  | ||||
| - name: Delete Python cache files | ||||
|   command: find . -type d -name __pycache__ -exec rm -r {} + | ||||
|   args: | ||||
|     chdir: "{{ project_path }}" | ||||
|   changed_when: false | ||||
|  | ||||
| - name: Install packages | ||||
|   command: pipenv install --python=/usr/bin/python3.6m --three --system --deploy | ||||
|  | ||||
| - name: Install development related packages | ||||
|   command: pipenv install --python=/usr/bin/python3.6m --three --system --deploy --dev | ||||
|   args: | ||||
|     chdir: "{{ project_path }}" | ||||
| @@ -107,7 +107,7 @@ class CalendarSocialApp(Flask, RoutedMixin): | ||||
|  | ||||
|         if current_user.is_authenticated and \ | ||||
|            not current_user.profile and \ | ||||
|            request.endpoint != 'account.first_steps': | ||||
|            request.endpoint != 'first_steps': | ||||
|             return redirect(url_for('account.first_steps')) | ||||
|  | ||||
|         return None | ||||
|   | ||||
| @@ -6,4 +6,4 @@ from calsocial import CalendarSocialApp | ||||
|  | ||||
| app = CalendarSocialApp('calsocial') | ||||
|  | ||||
| app.run() | ||||
| app.run(host='0.0.0.0', port=80) | ||||
|   | ||||
| @@ -46,7 +46,7 @@ class AccountBlueprint(Blueprint, RoutedMixin): | ||||
|         app.register_blueprint(self, url_prefix=url_prefix) | ||||
|  | ||||
|     @staticmethod | ||||
|     @RoutedMixin.route('/register', methods=['POST', 'GET']) | ||||
|     @RoutedMixin.route('/register') | ||||
|     def register_account(): | ||||
|         """View for user registration | ||||
|  | ||||
|   | ||||
| @@ -21,7 +21,7 @@ from datetime import timedelta | ||||
| import pickle | ||||
| from uuid import uuid4 | ||||
|  | ||||
| from flask import has_request_context, request as flask_request, session as flask_session | ||||
| from flask import current_app, has_request_context, request, session | ||||
| from flask.sessions import SessionInterface, SessionMixin | ||||
| from flask_caching import Cache | ||||
| from werkzeug.datastructures import CallbackDict | ||||
| @@ -46,7 +46,7 @@ class CachedSession(CallbackDict, SessionMixin):  # pylint: disable=too-many-anc | ||||
|             self.__modifying = True | ||||
|  | ||||
|             if has_request_context(): | ||||
|                 self['ip'] = flask_request.remote_addr | ||||
|                 self['ip'] = request.remote_addr | ||||
|  | ||||
|             self.modified = True | ||||
|  | ||||
| @@ -59,9 +59,6 @@ class CachedSession(CallbackDict, SessionMixin):  # pylint: disable=too-many-anc | ||||
|  | ||||
|     @property | ||||
|     def user(self): | ||||
|         """The user this session belongs to | ||||
|         """ | ||||
|  | ||||
|         from calsocial.models import User | ||||
|  | ||||
|         if 'user_id' not in self: | ||||
| @@ -90,11 +87,11 @@ class CachedSessionInterface(SessionInterface): | ||||
|         return str(uuid4()) | ||||
|  | ||||
|     @staticmethod | ||||
|     def get_cache_expiration_time(app, sess): | ||||
|     def get_cache_expiration_time(app, session): | ||||
|         """Get the expiration time of the cache entry | ||||
|         """ | ||||
|  | ||||
|         if sess.permanent: | ||||
|         if session.permanent: | ||||
|             return app.permanent_session_lifetime | ||||
|  | ||||
|         return timedelta(days=1) | ||||
| @@ -150,10 +147,7 @@ class CachedSessionInterface(SessionInterface): | ||||
|                             domain=domain) | ||||
|  | ||||
|     def delete_session(self, sid): | ||||
|         """Delete the session with ``sid`` as its session ID | ||||
|         """ | ||||
|  | ||||
|         if has_request_context() and flask_session.sid == sid: | ||||
|         if has_request_context() and session.sid == sid: | ||||
|             raise ValueError('Will not delete the current session') | ||||
|  | ||||
|         cache.delete(self.prefix + sid) | ||||
|   | ||||
| @@ -219,7 +219,7 @@ class GregorianCalendar(CalendarSystem): | ||||
|         end_timestamp = start_timestamp + timedelta(days=1) | ||||
|  | ||||
|         events = events.filter((Event.start_time <= end_timestamp) & | ||||
|                                (Event.end_time >= start_timestamp)) \ | ||||
|                                 (Event.end_time >= start_timestamp)) \ | ||||
|                        .order_by('start_time', 'end_time') | ||||
|  | ||||
|         if user is None: | ||||
|   | ||||
| @@ -89,9 +89,6 @@ class RoutedMixin: | ||||
|     """ | ||||
|  | ||||
|     def register_routes(self): | ||||
|         """Register all routes that were marked with :meth:`route` | ||||
|         """ | ||||
|  | ||||
|         for attr_name in self.__dir__(): | ||||
|             attr = getattr(self, attr_name) | ||||
|  | ||||
|   | ||||
| @@ -17,8 +17,6 @@ | ||||
| """Helper functions and fixtures for testing | ||||
| """ | ||||
|  | ||||
| from contextlib import contextmanager | ||||
|  | ||||
| import pytest | ||||
|  | ||||
| import calsocial | ||||
| @@ -72,22 +70,3 @@ def database(): | ||||
|         yield db | ||||
|  | ||||
|         db.drop_all() | ||||
|  | ||||
|  | ||||
| @contextmanager | ||||
| def alter_config(app, **kwargs): | ||||
|     saved = {} | ||||
|  | ||||
|     for key, value in kwargs.items(): | ||||
|         if key in app.config: | ||||
|             saved[key] = app.config[key] | ||||
|  | ||||
|         app.config[key] = value | ||||
|  | ||||
|     yield | ||||
|  | ||||
|     for key, value in kwargs.items(): | ||||
|         if key in saved: | ||||
|             app.config[key] = saved[key] | ||||
|         else: | ||||
|             del app.config[key] | ||||
|   | ||||
| @@ -25,4 +25,4 @@ def test_index_no_login(client): | ||||
|     """ | ||||
|  | ||||
|     page = client.get('/') | ||||
|     assert b'Peek inside' in page.data | ||||
|     assert b'Welcome to Calendar.social' in page.data | ||||
|   | ||||
| @@ -23,6 +23,14 @@ from calsocial.models import db, User | ||||
| from helpers import client, login | ||||
|  | ||||
|  | ||||
| def test_index_no_login(client): | ||||
|     """Test the main page without logging in | ||||
|     """ | ||||
|  | ||||
|     page = client.get('/') | ||||
|     assert b'Welcome to Calendar.social' in page.data | ||||
|  | ||||
|  | ||||
| def test_login_invalid_user(client): | ||||
|     """Test logging in with a non-existing user | ||||
|     """ | ||||
| @@ -73,4 +81,4 @@ def test_login_first_steps(client): | ||||
|     assert page.location == 'http://localhost/' | ||||
|  | ||||
|     page = client.get('/') | ||||
|     assert page.location == 'http://localhost/accounts/first-steps' | ||||
|     assert page.location == 'http://localhost/first-steps' | ||||
|   | ||||
| @@ -20,28 +20,28 @@ | ||||
| import calsocial | ||||
| from calsocial.models import db, User | ||||
|  | ||||
| from helpers import alter_config, client | ||||
| from helpers import client | ||||
|  | ||||
|  | ||||
| def test_register_page(client): | ||||
|     """Test the registration page | ||||
|     """ | ||||
|  | ||||
|     page = client.get('/accounts/register') | ||||
|     page = client.get('/register') | ||||
|     assert b'Register</button>' in page.data | ||||
|  | ||||
| def test_register_post_empty(client): | ||||
|     """Test sending empty registration data | ||||
|     """ | ||||
|  | ||||
|     page = client.post('/accounts/register', data={}) | ||||
|     page = client.post('/register', data={}) | ||||
|     assert b'This field is required' in page.data | ||||
|  | ||||
| def test_register_invalid_email(client): | ||||
|     """Test sending an invalid email address | ||||
|     """ | ||||
|  | ||||
|     page = client.post('/accounts/register', data={ | ||||
|     page = client.post('/register', data={ | ||||
|         'username': 'test', | ||||
|         'email': 'test', | ||||
|         'password': 'password', | ||||
| @@ -53,7 +53,7 @@ def test_register_password_mismatch(client): | ||||
|     """Test sending different password for registration | ||||
|     """ | ||||
|  | ||||
|     page = client.post('/accounts/register', data={ | ||||
|     page = client.post('/register', data={ | ||||
|         'username': 'test', | ||||
|         'email': 'test@example.com', | ||||
|         'password': 'password', | ||||
| @@ -65,12 +65,13 @@ def test_register(client): | ||||
|     """Test user registration | ||||
|     """ | ||||
|  | ||||
|     page = client.post('/accounts/register', data={ | ||||
|     page = client.post('/register', data={ | ||||
|         'username': 'test', | ||||
|         'email': 'test@example.com', | ||||
|         'password': 'password', | ||||
|         'password_retype': 'password', | ||||
|     }) | ||||
|     print(page.data) | ||||
|     assert page.status_code == 302 | ||||
|     assert page.location == 'http://localhost/' | ||||
|  | ||||
| @@ -89,7 +90,7 @@ def test_register_existing_username(client): | ||||
|         db.session.add(user) | ||||
|         db.session.commit() | ||||
|  | ||||
|     page = client.post('/accounts/register', data={ | ||||
|     page = client.post('/register', data={ | ||||
|         'username': 'test', | ||||
|         'email': 'test2@example.com', | ||||
|         'password': 'password', | ||||
| @@ -106,16 +107,10 @@ def test_register_existing_email(client): | ||||
|         db.session.add(user) | ||||
|         db.session.commit() | ||||
|  | ||||
|     page = client.post('/accounts/register', data={ | ||||
|     page = client.post('/register', data={ | ||||
|         'username': 'tester', | ||||
|         'email': 'test@example.com', | ||||
|         'password': 'password', | ||||
|         'password_retype': 'password', | ||||
|     }) | ||||
|     assert b'This email address can not be used' in page.data | ||||
|  | ||||
|  | ||||
| def test_registration_disabled(client): | ||||
|     with alter_config(calsocial.app, REGISTRATION_ENABLED=False): | ||||
|         page = client.get('/accounts/register') | ||||
|         assert b'Registration is disabled' in page.data | ||||
|   | ||||
		Reference in New Issue
	
	Block a user