Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor the backend in Python #107

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
# development setup #
#####################

# These are needed for docker-compose
# Thus we need to specify --env-file .env.development when calling docker-compose
REACT_PORT=3000 # local port for frontend
NODE_PORT=3001 # local port for backend
FLASK_RUN_PORT=3001 # local port for backend
SERVER_NAME=yourdomainename.com # your domain name, for the nginx configuration
MODE=dev|prod # you must chose !

#################
# backend setup #
Expand All @@ -15,7 +18,7 @@ DB_NAME=XXX
DB_PORT=XXX

# cors
CLIENT_ORIGIN=http://localhost:$REACT_PORT # URL of the frontend, to make a cors exception
CLIENT_ORIGIN=http://<name_of_the_frontend_container_in_docker_network|localhost>:$REACT_PORT # URL of the frontend, to make a cors exception

# bcrypt
SALT_ROUND=10
Expand All @@ -28,3 +31,13 @@ JWT_EXPIRATION="3d"
MAX_TITLE=200
MAX_SUMMARY=200
MAX_BODY=10000

##################
# frontend setup #
##################
# REACT will ignore variables that don't start with REACT_APP_ except NODE_ENV
# Thus, all the backend secrets are protected even if we load this file in the frontend app

NODE_ENV=development
REACT_APP_FILE_SIZE_MAX=40000
REACT_APP_API_URL="http://<name of the backend container in docker network | localhost>:$FLASK_RUN_PORT"
15 changes: 15 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!--
IMPORTANT NOTE FOR CONTRIBUTORS:

if the CI fails with the following error,
```
[warn] Code style issues found in the above file. Forgot to run Prettier?
```
please run
```
npm run pretty
```
in your local copy of `iscsc/iscsc.fr` and commit and push the fixes :yum:

NOTE: you'll need to have `npm` installed and to install `prettier` first: `npm install -g prettier`
-->
2 changes: 1 addition & 1 deletion .github/workflows/prettier.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ jobs:
uses: creyD/prettier_action@v4.3
with:
# This part is also where you can pass other options, for example:
prettier_options: --check **/*.{js,md,yml,css,html}
prettier_options: --check **/*.{js,md,yml,css,html} !mongodb/**
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ certbot
node_modules
package-lock.json
team.txt
mongodb/**
70 changes: 62 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,29 +48,79 @@ You should be able to delete any article **you created** from the database.

## Deployment

You need to set up the frontend and backend applications to test the server.
You need to setup 3 things to run the website:

- `.env.*` file
- MongoDB host folder
- start frontend, backend and database

For deployment, `development` and `production` modes are available

> ##### Notes for the iScsc members:
>
> Send me a message and I'll send you back a crypted version of the official `.env.production` and `.env.development` files.
> Send me a message and I'll send you back an encrypted version of the official `.env.production` and `.env.development` files.

Here is a quick guide after cloning the repository:

### Setup the local MongoDB folder

To make the database persistent through containers starting and stopping the database folder is shared with the host using a `docker` volume, you can see it in the [docker compose files](./docker-compose.yml).

> :warning::warning: **IMPORTANT**: the following script will give rwx permissions on the DB folder to the UID 1001 due to bitnami/mongodb image [constraint](https://hub.docker.com/r/bitnami/mongodb) (the _Note_ under "Persisting your database"), if, on your systemn, it already exists and shouldn't have these access please consider modifying the image!

However because the bitnami/mongodb container is a non-root container we've got to setup the right permission on that folder.
To set it up just run

```bash
./scripts/setup-db-folder.sh
```

### Development mode

#### .env file
You have two choices to run the development mode:

- with [`docker`](#docker)
- [manually](#manually-on-host) start the backend, frontend and setup a DB

#### .env.development file

Before deploying the application, you need to set the environment variables
From the root directory of the repository, do the following:

```
```bash
cp .env.example .env.development
```

After copying the example config of `.env`, you must fill in the missing information in this file. Check the example for more information.

#### Backend
#### Docker

Once your `.env.development` is [ready](#envdevelopment-file), run

```bash
docker-compose --env-file .env.development --file docker-compose-dev.yml up -d --build
```

> Make sure the `docker` daemon is running, or start it with `systemctl start docker`

The website is now up on `$CLIENT_URL` (specified in the `.env.development` file)

To see the running application, and check the logs use

```bash
docker ps
docker logs <CONTAINER_ID>
```

Finally, you can stop it with

```bash
docker-compose --env-file .env.development --file docker-compose-dev.yml down
```

#### Manually on host

##### Backend

From the root directory of the repository, do the following:

Expand All @@ -82,7 +132,7 @@ npm run dev

> You will need `nodemon` to run the backend. Use `npm install -g nodemon` to install it. Make sure you're supporting at least 2.0.20 with `nodemon --version`. Nodemon has been tested working fine with node 19.

#### Frontend
##### Frontend

From the root directory of the repository, do the following:

Expand All @@ -94,14 +144,18 @@ npm run start

Make sure your're using at least version 8.19.2 by checking `npm --version`, and update if needed with `npm update`.

##### Database

Start a MongoDB either in a container and expose a port or directly on your host with the right port configured. Then setup properly the .env, it should work but this is untested.

### Production mode

The production mode allows to deploy the application on the server. To use it, you will need:

- `docker`
- `docker-compose`

#### .env file
#### .env.production file

Before deploying the application, you need to set the environment variables, as for `developement` mode.

Expand Down Expand Up @@ -159,7 +213,7 @@ Once everything is ready, run
sudo docker-compose --env-file .env.production up -d --build
```

> Make sure the `docker` daemon is running, or start it with `sudo dockerd`
> Make sure the `docker` daemon is running, or start it with `systemctl start docker`

You application can now be started on `$CLIENT_URL` (specified in the `.env.production` file)

Expand Down
5 changes: 2 additions & 3 deletions backend/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
build
node_modules
.env
__pycache__/**

9 changes: 5 additions & 4 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
FROM node:18
FROM python:3.10.9

WORKDIR /backend
COPY package.json .
RUN npm install

COPY . .
CMD node app.js
RUN pip install -r requirements.txt

CMD flask run
8 changes: 8 additions & 0 deletions backend/Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
FROM python:3.10.9

WORKDIR /backend

COPY requirements.txt .
RUN pip install -r requirements.txt

CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"]
6 changes: 3 additions & 3 deletions backend/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const articleRoutes = require('./routes/articles')
const httpServer = createServer(app)
require('dotenv').config({ path: `../.env.${process.env.NODE_ENV}` })

const { DB_PORT, DB_NAME, NODE_PORT, CLIENT_ORIGIN } = process.env
const { DB_PORT, DB_NAME, FLASK_RUN_PORT, CLIENT_ORIGIN } = process.env

app.use(cors({ origin: CLIENT_ORIGIN || 'http://localhost:3000' }))
app.use(express.json({ limit: '1MB' }))
Expand All @@ -23,8 +23,8 @@ uri = `mongodb://mongodb:${DB_PORT}/${DB_NAME}?retryWrites=true&w=majority`
mongoose
.connect(uri)
.then(() => {
httpServer.listen(NODE_PORT || 3001, () => {
console.log(`Server listening: http://localhost:${NODE_PORT}`)
httpServer.listen(FLASK_RUN_PORT || 3001, () => {
console.log(`Server listening: http://localhost:${FLASK_RUN_PORT}`)
})
})
.catch(err => {
Expand Down
14 changes: 14 additions & 0 deletions backend/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from flask import Flask
from routes.articles import articles
from routes.users import users

app = Flask(__name__)


@app.route("/")
def get_main():
return "iscsc.fr backend is running"


app.register_blueprint(articles, url_prefix="/api/articles")
app.register_blueprint(users, url_prefix="/api/users")
42 changes: 42 additions & 0 deletions backend/controllers/articleController.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from flask import jsonify
from .dummy_data import *

# TODO: Implement auth middleware logic to check if user is logged in


# TODO: fetch one article from database
def get_article(id):
if id == "1":
return jsonify(dummy_article), 200
elif id == "2":
return jsonify(dummy_article_2), 200
else:
return "", 404


# TODO: create one article
def post_article():
return jsonify({"success": True}), 200


# TODO: modify one article in the database
def put_article():
return jsonify({"success": True}), 200


# TODO: delete one article from database
def delete_article(id):
return jsonify({"success": True}), 200


# TODO: fetch all articles from database
def get_all_articles():
return jsonify([dummy_article, dummy_article_2]), 200


# TODO: fetch one article from database by author
def get_article_by_author(author):
if author == "ctmbl":
return jsonify([dummy_article, dummy_article_2]), 200
else:
return "", 404
20 changes: 20 additions & 0 deletions backend/controllers/dummy_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
dummy_article = {
"author": "ctmbl",
"title": "My dummy article",
"body": "This is a dummy article hardcoded to build the backend",
"_id": "1",
}

dummy_article_2 = {
"author": "ctmbl",
"title": "My dummy article 2",
"body": "This is a dummy article 2",
"_id": "2",
}

dummy_user = {
"name": "atxr",
"email": "atxr@iscsc.fr",
"password": "123456",
"_id": "1",
}
20 changes: 20 additions & 0 deletions backend/controllers/userController.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from flask import jsonify
from flask import request


# TODO: log existing user
def login():
if (
"name" in request.form
and "password" in request.form
and request.form["name"] == "atxr"
and request.form["password"] == "123456"
):
return jsonify({"success": request.form["name"] == "atxr"}), 200
else:
return jsonify({"success": False}), 401


# TODO: create new user
def signup():
return jsonify({"success": True}), 200
1 change: 1 addition & 0 deletions backend/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
flask==2.2.3
18 changes: 18 additions & 0 deletions backend/routes/articles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from flask import Blueprint
from controllers.articleController import *

articles = Blueprint("articles", "backend") # FIXME: don't hardcode backend

articles.add_url_rule("/", "get_all_articles", get_all_articles, methods=["GET"])
articles.add_url_rule("/<id>", "get_article", get_article, methods=["GET"])
articles.add_url_rule("/", "post_article", post_article, methods=["POST"])
articles.add_url_rule("/<id>", "delete_article", delete_article, methods=["DELETE"])
articles.add_url_rule(
"/by-author/<author>",
"get_article_by_author",
get_article_by_author,
methods=["GET"],
)

# Not implemented yet in the frontend
# articles.add_url_rule(put_article, "/<id>", methods=["PUT"])
11 changes: 11 additions & 0 deletions backend/routes/users.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from flask import Blueprint
from controllers.userController import *

users = Blueprint("users", "backend") # FIXME: don't hardcode backend

users.add_url_rule("/login", "login", login, methods=["POST"])
users.add_url_rule("/signup", "signup", signup, methods=["POST"])

# Not implemented yet in the frontend
# users.add_url_rule(get_user, "/<id>", methods=["GET"])
# users.add_url_rule(put_user, "/<id>", methods=["PUT"])
Loading