Compare commits
11 Commits
f14085f4c1
...
1c57950558
Author | SHA1 | Date | |
---|---|---|---|
1c57950558 | |||
f132cdbeef | |||
0387c05055 | |||
552b2ffc47 | |||
a2e859af5d | |||
81b09190de | |||
ed100ee9e5 | |||
5dc6c4998d | |||
0d68233d41 | |||
4caac25b14 | |||
3defe020f5 |
21
README.md
21
README.md
@ -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/)
|
- [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.
|
- 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.
|
||||||
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.
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -20,6 +20,11 @@ server {
|
|||||||
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
|
||||||
|
@ -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>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
try:
|
||||||
mail.send(email)
|
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}')
|
@ -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>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
try:
|
||||||
mail.send(email)
|
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>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
try:
|
||||||
mail.send(email)
|
mail.send(email)
|
||||||
print('Password', new_password)
|
except SMTPException as exception:
|
||||||
print('Reset Token', self.reset_token)
|
write('system.log', f'SMTP Error while trying to reset password for {self.get_username()} with error: {exception}')
|
||||||
print('Verification Token', self.verification_token)
|
return jsonify({'error': f'SMTP Error: {exception}'}), 500
|
||||||
print('Reset Link', f'{url_for("admin._reset", token=self.reset_token, verification=self.verification_token, _external=True)}')
|
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>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
try:
|
||||||
mail.send(email)
|
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>
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
try:
|
||||||
mail.send(message)
|
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.'
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user