Compare commits

...

11 Commits

7 changed files with 61 additions and 56 deletions

View File

@ -29,12 +29,11 @@ To set up the server, consult some of the comprehensive guides on various hostin
Here is a [good starting point on setting up a server](https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-22-04). Here is a [good starting point on setting up a server](https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-22-04).
To install Docker and Docker Compose, consult the respective documentation: To install Docker and Docker Compose, consult the respective documentation:
- [Install on Ubuntu](https://docs.docker.com/engine/install/ubuntu/) or [Install on Debian](https://docs.docker.com/engine/install/debian/)
- Docker Compose should be installed as part of that.
``` - [Install on Ubuntu](https://docs.docker.com/engine/install/ubuntu/) or [Install on Debian](https://docs.docker.com/engine/install/debian/)
At the time of writing, there has been an upgrade to Docker and Docker Compose, meaning the syntax below might be different between versions. - Docker Compose should be installed as part of that.
```
> At the time of writing, there has been an upgrade to Docker and Docker Compose, meaning the syntax below might be different between versions.
Check if Git is installed on your server using the `git --version` command. Check if Git is installed on your server using the `git --version` command.
If it isn't installed, install it. If it isn't installed, install it.
@ -72,13 +71,13 @@ Also make sure that the various entries for usernames and passwords match.
There are some values in the following four files you will need to configure to reflect the domain you are installing this app. There are some values in the following four files you will need to configure to reflect the domain you are installing this app.
``` ```sh
# .env # .env
SERVER_NAME= # URL where this will be hosted. SERVER_NAME= # URL where this will be hosted.
``` ```
``` ```sh
# install-script.sh # install-script.sh
domains=(example.org www.example.org) domains=(example.org www.example.org)
@ -87,7 +86,7 @@ email="" # Adding a valid address is strongly recommended
Substitute the domain name `domain_name` in the two file paths in the following file: Substitute the domain name `domain_name` in the two file paths in the following file:
``` ```sh
# nginx/ssl.conf # nginx/ssl.conf
ssl_certificate /etc/letsencrypt/live/domain_name/fullchain.pem; ssl_certificate /etc/letsencrypt/live/domain_name/fullchain.pem;
@ -95,9 +94,9 @@ ssl_certificate_key /etc/letsencrypt/live/domain_name/privkey.pem;
... ...
``` ```
And **six** locations in the following file, two for the regular version of the domain and two for the www version: And **six** locations in the following file, two for the regular version of the domain and four for the www version (remember to keep the www. prefix where present):
``` ```nginx
# nginx/conf.d/ref-test-app.conf # nginx/conf.d/ref-test-app.conf
server { server {
@ -140,9 +139,9 @@ This will be set up automatically.
However, there is a specific chicken-and-egg problem as the web server, Nginx, won't run without certificates, Certbot, the certificate generator, won't run without the web server. However, there is a specific chicken-and-egg problem as the web server, Nginx, won't run without certificates, Certbot, the certificate generator, won't run without the web server.
So to solve this, there is an automation script we can run that will set up a dummy certificate and then issue the appropriate certificates for us. So to solve this, there is an automation script we can run that will set up a dummy certificate and then issue the appropriate certificates for us.
``` ```sh
$ chmod +x install-script.sh chmod +x install-script.sh
$ sudo ./install-script.sh sudo ./install-script.sh
``` ```
This will take a long time to run the first time because it will try and generate a fairly sizeable cypher. This will take a long time to run the first time because it will try and generate a fairly sizeable cypher.

View File

@ -6,11 +6,6 @@
- [Docker Compose Reference](https://docs.docker.com/compose/compose-file/compose-file-v3/) - [Docker Compose Reference](https://docs.docker.com/compose/compose-file/compose-file-v3/)
### MongoDB/PyMongo
- [MongoDB Shell Commands](https://docs.mongodb.com/manual/reference/)
- [PyMongo Driver](https://pymongo.readthedocs.io/en/stable/)
## Source Code ## Source Code
- [MongoDB Docker Image entrypoint shell script](https://github.com/docker-library/mongo/blob/master/5.0/docker-entrypoint.sh) (Context: Tried to replicate the command to create a new user in the original entrypoint script in the custom initialisation script in this app.) - [MongoDB Docker Image entrypoint shell script](https://github.com/docker-library/mongo/blob/master/5.0/docker-entrypoint.sh) (Context: Tried to replicate the command to create a new user in the original entrypoint script in the custom initialisation script in this app.)
@ -23,15 +18,6 @@
- [Tables](https://www.blog.pythonlibrary.org/2017/12/14/flask-101-adding-editing-and-displaying-data/) - [Tables](https://www.blog.pythonlibrary.org/2017/12/14/flask-101-adding-editing-and-displaying-data/)
- [Tables, but interactive](https://blog.miguelgrinberg.com/post/beautiful-interactive-tables-for-your-flask-templates) - [Tables, but interactive](https://blog.miguelgrinberg.com/post/beautiful-interactive-tables-for-your-flask-templates)
## Stack Exchange/Overflow
### MongoDB
- [Creating MongoDB Database on Container Start](https://stackoverflow.com/questions/42912755/how-to-create-a-db-for-mongodb-container-on-start-up)
- [Passing Environment Variables to Docker Container Entrypoint](https://stackoverflow.com/questions/64606674/how-can-i-pass-environment-variables-to-mongo-docker-entrypoint-initdb-d)
- [Integrating Flask-Login with MongoDB](https://stackoverflow.com/questions/54992412/flask-login-usermixin-class-with-a-mongodb) (**This does not work with the app as is, and is possibly something that needs more research and development in the future**)
- [Setting up a Postfix email notification system](https://medium.com/@vietgoeswest/a-simple-outbound-email-service-for-your-app-in-15-minutes-cc4da70a2af7)
## YouTube Tutorials ## YouTube Tutorials
### General Flask Introduction ### General Flask Introduction
@ -72,7 +58,7 @@ A much simpler and more rudimentary introduction to Flask and MongoDB.
- [Build a User Login System with `flask-login`, `flask-wtforms`, `flask-bootstrap`, and `flask-sqlalchemy`](https://www.youtube.com/watch?v=8aTnmsDMldY) - [Build a User Login System with `flask-login`, `flask-wtforms`, `flask-bootstrap`, and `flask-sqlalchemy`](https://www.youtube.com/watch?v=8aTnmsDMldY)
A much more robust method that uses the various Flask modules to make a more powerful framework. A much more robust method that uses the various Flask modules to make a more powerful framework.
Uses SQL rather than MongoDB. Uses SQL.
### Flask techniques ### Flask techniques
@ -80,4 +66,4 @@ Uses SQL rather than MongoDB.
### Flask handling file uploads ### Flask handling file uploads
- [Handlin File Uploads](https://blog.miguelgrinberg.com/post/handling-file-uploads-with-flask) - [Handling File Uploads](https://blog.miguelgrinberg.com/post/handling-file-uploads-with-flask)

View File

@ -10,11 +10,12 @@ services:
volumes: volumes:
- ./certbot:/etc/letsencrypt:ro - ./certbot:/etc/letsencrypt:ro
- ./nginx:/etc/nginx - ./nginx:/etc/nginx
- ./src/html:/usr/share/nginx/html/ - ./src/html/robots.txt:/usr/share/nginx/html/robots.txt:ro
- ./ref-test/app/editor/static:/usr/share/nginx/html/admin/editor/static - ./ref-test/app/root:/usr/share/nginx/html/root:ro
- ./ref-test/app/admin/static:/usr/share/nginx/html/admin/static - ./ref-test/app/admin/static:/usr/share/nginx/html/admin/static:ro
- ./ref-test/app/quiz/static:/usr/share/nginx/html/quiz/static - ./ref-test/app/editor/static:/usr/share/nginx/html/editor/static:ro
- ./ref-test/app/root:/usr/share/nginx/html/root - ./ref-test/app/quiz/static:/usr/share/nginx/html/quiz/static:ro
- ./ref-test/app/view/static:/usr/share/nginx/html/view/static:ro
ports: ports:
- 80:80 - 80:80
- 443:443 - 443:443

View File

@ -15,11 +15,16 @@ server {
listen 443 ssl http2 default_server; listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server; listen [::]:443 ssl http2 default_server;
# SSL configuration # SSL configuration
include /etc/nginx/ssl.conf; include /etc/nginx/ssl.conf;
include /etc/nginx/certbot-challenge.conf; include /etc/nginx/certbot-challenge.conf;
# Define locations for static files to be served by Nginx # Define locations for static files to be served by Nginx
location ^~ /root/ {
include /etc/nginx/mime.types;
alias /usr/share/nginx/html/root/;
}
location ^~ /quiz/static/ { location ^~ /quiz/static/ {
include /etc/nginx/mime.types; include /etc/nginx/mime.types;
alias /usr/share/nginx/html/quiz/static/; alias /usr/share/nginx/html/quiz/static/;
@ -32,12 +37,12 @@ server {
location ^~ /admin/editor/static/ { location ^~ /admin/editor/static/ {
include /etc/nginx/mime.types; include /etc/nginx/mime.types;
alias /usr/share/nginx/html/admin/editor/static/; alias /usr/share/nginx/html/editor/static/;
} }
location ^~ /admin/view/static/ { location ^~ /admin/view/static/ {
include /etc/nginx/mime.types; include /etc/nginx/mime.types;
alias /usr/share/nginx/html/admin/view/static/; alias /usr/share/nginx/html/view/static/;
} }
# Proxy to the main app for all other requests # Proxy to the main app for all other requests

View File

@ -6,6 +6,7 @@ from .test import Test
from flask_login import current_user from flask_login import current_user
from flask_mail import Message from flask_mail import Message
from smtplib import SMTPException
from datetime import datetime, timedelta from datetime import datetime, timedelta
from uuid import uuid4 from uuid import uuid4
@ -174,4 +175,7 @@ class Entry(db.Model):
<p>Best wishes, <br/> SKA Refereeing</p> <p>Best wishes, <br/> SKA Refereeing</p>
""" """
) )
mail.send(email) try:
mail.send(email)
except SMTPException as exception:
write('system.log', f'SMTP Error when trying to notify results to {self.get_surname()}, {self.get_first_name()} with error: {exception}')

View File

@ -6,6 +6,7 @@ from flask import flash, jsonify, session
from flask.helpers import url_for from flask.helpers import url_for
from flask_login import current_user, login_user, logout_user, UserMixin from flask_login import current_user, login_user, logout_user, UserMixin
from flask_mail import Message from flask_mail import Message
from smtplib import SMTPException
from werkzeug.security import check_password_hash, generate_password_hash from werkzeug.security import check_password_hash, generate_password_hash
import secrets import secrets
@ -90,7 +91,10 @@ class User(UserMixin, db.Model):
<p>SKA Refereeing</p> <p>SKA Refereeing</p>
""" """
) )
mail.send(email) try:
mail.send(email)
except SMTPException as exception:
write('system.log', f'SMTP Error while trying to notify new user account creation to {self.get_username()} with error: {exception}')
return True, f'User {self.get_username()} was created successfully.' return True, f'User {self.get_username()} was created successfully.'
def login(self, remember:bool=False): def login(self, remember:bool=False):
@ -109,7 +113,6 @@ class User(UserMixin, db.Model):
self.set_password(new_password) self.set_password(new_password)
self.reset_token = secrets.token_urlsafe(16) self.reset_token = secrets.token_urlsafe(16)
self.verification_token = secrets.token_urlsafe(16) self.verification_token = secrets.token_urlsafe(16)
db.session.commit()
email = Message( email = Message(
subject='RefTest | Password Reset', subject='RefTest | Password Reset',
recipients=[self.get_email()], recipients=[self.get_email()],
@ -142,11 +145,12 @@ class User(UserMixin, db.Model):
<p>SKA Refereeing</p> <p>SKA Refereeing</p>
""" """
) )
mail.send(email) try:
print('Password', new_password) mail.send(email)
print('Reset Token', self.reset_token) except SMTPException as exception:
print('Verification Token', self.verification_token) write('system.log', f'SMTP Error while trying to reset password for {self.get_username()} with error: {exception}')
print('Reset Link', f'{url_for("admin._reset", token=self.reset_token, verification=self.verification_token, _external=True)}') return jsonify({'error': f'SMTP Error: {exception}'}), 500
db.session.commit()
return jsonify({'success': 'Your password reset link has been generated.'}), 200 return jsonify({'success': 'Your password reset link has been generated.'}), 200
def clear_reset_tokens(self): def clear_reset_tokens(self):
@ -182,7 +186,10 @@ class User(UserMixin, db.Model):
<p>SKA Refereeing</p> <p>SKA Refereeing</p>
""" """
) )
mail.send(email) try:
mail.send(email)
except SMTPException as exception:
write('system.log', f'SMTP Error when trying to delete account {username} with error: {exception}')
return True, message return True, message
def update(self, password:str=None, email:str=None, notify:bool=False): def update(self, password:str=None, email:str=None, notify:bool=False):
@ -223,5 +230,8 @@ class User(UserMixin, db.Model):
<p>SKA Refereeing</p> <p>SKA Refereeing</p>
""" """
) )
mail.send(message) try:
mail.send(message)
except SMTPException as exception:
write('system.log', f'SMTP Error when trying to update account {self.get_username()} with error: {exception}')
return True, f'Account {self.get_username()} has been updated.' return True, f'Account {self.get_username()} has been updated.'

View File

@ -1,13 +1,13 @@
blinker==1.4 blinker==1.5
cffi==1.15.0 cffi==1.15.1
click==8.1.3 click==8.1.3
cryptography==37.0.2 cryptography==37.0.4
dnspython==2.2.1 dnspython==2.2.1
dominate==2.6.0 dominate==2.7.0
email-validator==1.2.1 email-validator==1.2.1
Flask==2.1.2 Flask==2.2.2
Flask-Bootstrap==3.3.7.1 Flask-Bootstrap==3.3.7.1
Flask-Login==0.6.1 Flask-Login==0.6.2
Flask-Mail==0.9.1 Flask-Mail==0.9.1
Flask-SQLAlchemy==2.5.1 Flask-SQLAlchemy==2.5.1
Flask-WTF==1.0.1 Flask-WTF==1.0.1
@ -20,8 +20,8 @@ MarkupSafe==2.1.1
pycparser==2.21 pycparser==2.21
python-dotenv==0.20.0 python-dotenv==0.20.0
six==1.16.0 six==1.16.0
SQLAlchemy==1.4.37 SQLAlchemy==1.4.40
SQLAlchemy-Utils==0.38.2 SQLAlchemy-Utils==0.38.3
visitor==0.1.3 visitor==0.1.3
Werkzeug==2.1.2 Werkzeug==2.2.2
WTForms==3.0.1 WTForms==3.0.1