Skip to content
This repository was archived by the owner on Nov 14, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 17 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,19 @@ Generate a backend and frontend stack using Python, including interactive API do
* Celery worker that can import and use models and code from the rest of the backend selectively (you don't have to install the complete app in each worker)
* REST backend tests based on Pytest, integrated with Docker, so you can test the full API interaction, independent on the database. As it runs in Docker, it can build a new data store from scratch each time (so you can use ElasticSearch, MongoDB, CouchDB, or whatever you want, and just test that the API works)
* Easy Python integration with Jupyter Kernels for remote or in-Docker development with extensions like Atom Hydrogen or Visual Studio Code Jupyter
* Angular frontend with:
* Docker server based on Nginx
* Vue frontend:
* Generated with Vue CLI
* JWT Authentication handling
* Login view
* After login, main dashboard view
* Vuex
* Vue-router
* Vuetify for beautiful material design components
* TypeScript
* Docker server based on Nginx (configured to play nicely with Vue-router)
* Docker multi-stage building, so you don't need to save or commit compiled code
* Docker building integrated tests with Chrome Headless
* Frontend tests ran at build time (can be disabled too)
* Made as modular as possible, so it works out of the box, but you can re-generate with Vue CLI or create it as you need, and re-use what you want
* PGAdmin for PostgreSQL database, you can modify it to use PHPMyAdmin and MySQL easily
* Swagger-UI for live interactive documentation
* Flower for Celery jobs monitoring
Expand Down Expand Up @@ -67,24 +76,22 @@ The input variables, with their default values (some auto generated) are:
* `project_slug`: The development friendly name of the project. By default, based on the project name
* `domain_main`: The domain in where to deploy the project for production (from the branch `production`), used by the load balancer, backend, etc. By default, based on the project slug.
* `domain_staging`: The domain in where to deploy while staging (before production) (from the branch `master`). By default, based on the main domain.
* `domain_branch`: The domain in where to deploy the project while on another branch, probably a feature branch. By default, based on the main domain.
* `domain_dev`: The domain to use while developing. It won't be deployed, but you should use it by modifying your local `hosts` file.

* `docker_swarm_stack_name_main`: The name of the stack while deploying to Docker in Swarm mode for production. By default, based on the domain.
* `docker_swarm_stack_name_staging`: The name of the stack while deploying to Docker in Swarm mode for staging. By default, based on the domain.
* `docker_swarm_stack_name_branch`: The name of the stack while deploying to Docker in Swarm mode for feature branches. By default, based on the domain.

* `secret_key`: Backend server secret key. Use the method above to generate it.
* `first_superuser`: The first superuser generated, with it you will be able to create more users, etc. By default, based on the domain.
* `first_superuser_password`: First superuser password. Use the method above to generate it.
* `backend_cors_origins`: Origins (domains, more or less) that are enabled for CORS (Cross Origin Resource Sharing). This allows a frontend in one domain (e.g. `https://dashboard.example.com`) to communicate with this backend, that could be living in another domain (e.g. `https://api.example.com`). It can also be used to allow your local frontend (with a custom `hosts` domain mapping, as described in the project's `README.md`) that could be living in `http://dev.example.com:8080` to cummunicate with the backend at `https://stag.example.com`. Notice the `http` vs `https` and the `dev.` prefix for local development vs the "staging" `stag.` prefix. By default, it includes origins for production, staging and development, with ports commonly used during local development by several popular frontend frameworks (Vue with `:8080`, React, Angular).

* `postgres_password`: Postgres database password. Use the method above to generate it. (You could easily modify it to use MySQL, MariaDB, etc).
* `pgadmin_default_user`: PGAdmin default user, to log-in to the PGAdmin interface.
* `pgadmin_default_user_password`: PGAdmin default user password. Generate it with the method above.

* `traefik_constraint_tag`: The tag to be used by the internal Traefik load balancer (for example, to divide requests between backend and frontend) for production. Used to separate this stack from any other stack you might have. This should identify each stack in each environment (production, staging, etc).
* `traefik_constraint_tag_staging`: The Traefik tag to be used while on staging.
* `traefik_constraint_tag_branch`: The Traefik tag to be used while on a feature branch.

* `traefik_public_network`: This assumes you have another separate publicly facing Traefik at the server / cluster level. This is the network that main Traefik lives in.
* `traefik_public_constraint_tag`: The tag that should be used by stack services that should communicate with the public.

Expand All @@ -105,10 +112,12 @@ Read the [**Guide to deploy a Docker Swarm Mode Cluster**](docker-swarm-cluster-

## More details

After using this generator, the new project will contain an extensive `README.md` with instructions for development, deployment, etc. You can read [the project `README.md` template here](./{{cookiecutter.project_slug}}/README.md).
After using this generator, your new project (the directory created) will contain an extensive `README.md` with instructions for development, deployment, etc. You can pre-read [the project `README.md` template here too](./{{cookiecutter.project_slug}}/README.md).

## History

**Note about Angular**: a previous version of this project generated a basic default Angular frontend application, but without any view or interaction with the rest of the stack (the backend API). I recently switched to Vue for frontend and used it to created the basic frontend views for this project (that didn't exist before). If you are interested in keeping the Angular version, let me know in an issue, I can create an Angular version of the project (without the current default views), then you can integrate your Angular app with the basic `Dockerfile` and additional files.

This project was based on [senseta-os/senseta-base-project](https://github.com/senseta-os/senseta-base-project).

As [I was the only maintainer](https://github.com/tiangolo), I'm continuing the development in this fork (https://github.com/tiangolo/full-stack).
Expand Down
2 changes: 2 additions & 0 deletions cookiecutter.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"secret_key": "changethis",
"first_superuser": "admin@{{cookiecutter.domain_main}}",
"first_superuser_password": "changethis",
"backend_cors_origins": "http://{{cookiecutter.domain_dev}}, http://{{cookiecutter.domain_dev}}:4200, http://{{cookiecutter.domain_dev}}:3000, http://{{cookiecutter.domain_dev}}:8080, https://{{cookiecutter.domain_staging}}, https://{{cookiecutter.domain_main}}",


"postgres_password": "changethis",
Expand All @@ -34,6 +35,7 @@

"_copy_without_render": [
"frontend/src/**/*.html",
"frontend/src/**/*.vue",
"frontend/node_modules/*"
]
}
1 change: 1 addition & 0 deletions {{cookiecutter.project_slug}}/.env
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
COMPOSE_PATH_SEPARATOR=:
COMPOSE_FILE=docker-compose.test.yml:docker-compose.shared.admin.yml:docker-compose.shared.base-images.yml:docker-compose.shared.depends.yml:docker-compose.shared.env.yml:docker-compose.dev.build.yml:docker-compose.dev.command.yml:docker-compose.dev.env.yml:docker-compose.dev.labels.yml:docker-compose.dev.networks.yml:docker-compose.dev.ports.yml:docker-compose.dev.volumes.yml
DOMAIN={{cookiecutter.domain_dev}}
BACKEND_CORS_ORIGINS={{cookiecutter.backend_cors_origins}}
TRAEFIK_TAG={{cookiecutter.traefik_constraint_tag}}
POSTGRES_SERVER=db
POSTGRES_USER=postgres
Expand Down
4 changes: 2 additions & 2 deletions {{cookiecutter.project_slug}}/.gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ tests:
build-stag:
stage: build
script:
- TAG=stag FRONTEND_ENV=stag sh ./script-build-push.sh
- TAG=stag FRONTEND_ENV=staging sh ./script-build-push.sh
only:
- master
tags:
Expand All @@ -30,7 +30,7 @@ build-stag:
build-prod:
stage: build
script:
- TAG=prod sh ./script-build-push.sh
- TAG=prod FRONTEND_ENV=production sh ./script-build-push.sh
only:
- production
tags:
Expand Down
70 changes: 31 additions & 39 deletions {{cookiecutter.project_slug}}/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,25 @@

* Update your local `hosts` file, set the IP `127.0.0.1` (your `localhost`) to `{{cookiecutter.domain_dev}}`. The `docker-compose.dev.env.yml` file(that will be one of the Docker Compose files used by default) will set the environment variable `SERVER_NAME` to that host. Otherwise you would receive 404 HTTP errors and "Cross Origin Resource Sharing" (CORS) errors.

* Modify your hosts file, for macOS and Linux, probably in `/etc/hosts`. For Windows, in `c:\Windows\System32\Drivers\etc\hosts` to include:
* Modify your `hosts` file, for macOS and Linux, probably in `/etc/hosts`. For Windows, in . Make sure you open it with administrative privileges.

**Note for Windows**: If you are in Windows, open the main Windows menu, search for "notepad", right click it, and select the option "open as Administrator" or similar. Then click the "File" menu, "Open file" and open the file at `c:\Windows\System32\Drivers\etc\hosts`.

Make sure the `hosts` file contains (additionally to whatever it has):

```
0.0.0.0 {{cookiecutter.domain_dev}}
127.0.0.1 {{cookiecutter.domain_dev}}
```

...that will make your browser talk to your locally running server when it is asked to go to `{{cookiecutter.domain_dev}}` and think that it is a remote server while it is actually running locally.
...that will make your browser talk to your locally running server when it is asked to go to `{{cookiecutter.domain_dev}}` and think that it is a remote server while it is actually running in your computer.

**Note for Windows and Mac**: If you are on Windows or Mac, and your Docker is running on a virtual machine (like with Docker Toolbox), you should put the IP of the virtual machine. For example:

```
192.168.99.100 {{cookiecutter.domain_dev}}
```

Make sure you open the `hosts` file with administrator privileges to be able to change it. And save it as is, without extensions (Windows tends to try to automatically add `.txt` extensions).
Make sure you save the file as is, without extensions (Windows tends to try to automatically add `.txt` extensions).

* Start the stack with Docker Compose:

Expand All @@ -32,7 +42,7 @@ PGAdmin, PostgreSQL web administration: http://{{cookiecutter.domain_dev}}:5050

Flower, administration of Celery tasks: http://{{cookiecutter.domain_dev}}:5555

Traefik UI, to see how the routes are being handled by the proxy: http://{{cookiecutter.domain_dev}}:8080
Traefik UI, to see how the routes are being handled by the proxy: http://{{cookiecutter.domain_dev}}:8090


## Backend local development, additional details
Expand Down Expand Up @@ -200,16 +210,22 @@ If you don't want to start with the default models and want to remove them / mod

## Frontend development

* Enter the `frontend` directory, install the NPM packages and start it the `npm` scrits:
* Enter the `frontend` directory, install the NPM packages and start the live server using the `npm` scripts:

```bash
cd frontend
npm install
npm run start
npm run serve
```

Then open your browser at http://{{cookiecutter.domain_dev}}:8080

Notice that this live server is not running inside Docker, it is for local development, and that is the recommended workflow. Once you are happy with your frontend, you can build the frontend Docker image and start it, to test it in a production-like environment. But compiling the image at every change will not be as productive as running the local development server.

Check the file `package.json` to see other available options.

If you have Vue CLI installed, you can also run `vue ui` to control, configure, serve and analyse your application using a nice local web user interface.

## Deployment

You can deploy the stack to a Docker Swarm mode cluster and use CI systems to do it automatically. But you have to configure a couple things first.
Expand Down Expand Up @@ -459,36 +475,10 @@ PGAdmin: http://{{cookiecutter.domain_dev}}:5050

Flower: http://{{cookiecutter.domain_dev}}:5555

Traefik UI: http://{{cookiecutter.domain_dev}}:8080

## Project Cookiecutter variables used during generation

* `project_name`: {{cookiecutter.project_name}}
* `project_slug`: {{cookiecutter.project_slug}}
* `domain_main`: {{cookiecutter.domain_main}}
* `domain_staging`: {{cookiecutter.domain_staging}}
* `domain_dev`: {{cookiecutter.domain_dev}}
* `docker_swarm_stack_name_main`: {{cookiecutter.docker_swarm_stack_name_main}}
* `docker_swarm_stack_name_staging`: {{cookiecutter.docker_swarm_stack_name_staging}}
* `secret_key`: {{cookiecutter.secret_key}}
* `first_superuser`: {{cookiecutter.first_superuser}}
* `first_superuser_password`: {{cookiecutter.first_superuser_password}}
* `postgres_password`: {{cookiecutter.postgres_password}}
* `pgadmin_default_user`: {{cookiecutter.pgadmin_default_user}}
* `pgadmin_default_user_password`: {{cookiecutter.pgadmin_default_user_password}}
* `traefik_constraint_tag`: {{cookiecutter.traefik_constraint_tag}}
* `traefik_constraint_tag_staging`: {{cookiecutter.traefik_constraint_tag_staging}}
* `traefik_public_network`: {{cookiecutter.traefik_public_network}}
* `traefik_public_constraint_tag`: {{cookiecutter.traefik_public_constraint_tag}}
* `flower_auth`: {{cookiecutter.flower_auth}}
* `sentry_dsn`: {{cookiecutter.sentry_dsn}}
* `docker_image_prefix`: {{cookiecutter.docker_image_prefix}}
* `docker_image_backend`: {{cookiecutter.docker_image_backend}}
* `docker_image_celeryworker`: {{cookiecutter.docker_image_celeryworker}}
* `docker_image_frontend`: {{cookiecutter.docker_image_frontend}}


## Updating, re-generating
Traefik UI: http://{{cookiecutter.domain_dev}}:8090


## Project generation and updating, or re-generating

This project was generated using https://github.com/tiangolo/full-stack with:

Expand All @@ -497,13 +487,15 @@ pip install cookiecutter
cookiecutter https://github.com/tiangolo/full-stack
```

You can check the variables used during generation in the file `cookiecutter-config-file.yml`.

You can generate the project again with the same configurations used the first time.

That would be useful if, for example, the project generator (`tiangolo/full-stack`) was updated and you want to integrate or review the changes.

You could generate a new project with the same configurations as this one in a parallel directory. And compare the differences between the two, without having to overwrite your current code and being able to use your current variables.
You could generate a new project with the same configurations as this one in a parallel directory. And compare the differences between the two, without having to overwrite your current code but being able to use the same variables used for your current project.

To achieve that, the generated project includes a file `cookiecutter-config-file.yml` with the current variables used.
To achieve that, the generated project includes the file `cookiecutter-config-file.yml` with the current variables used.

You can use that file while generating a new project to reuse all those variables.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def getenv_boolean(var_name, default_value=False):
ACCESS_TOKEN_EXPIRE_MINUTES = 60 * 24 * 8 # 60 minutes * 24 hours * 8 days

SERVER_NAME = os.getenv("SERVER_NAME")
BACKEND_CORS_ORIGINS = os.getenv("BACKEND_CORS_ORIGINS")
SENTRY_DSN = os.getenv("SENTRY_DSN")

POSTGRES_SERVER = os.getenv("POSTGRES_SERVER")
Expand Down
18 changes: 12 additions & 6 deletions {{cookiecutter.project_slug}}/backend/app/app/core/cors.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,15 @@
from app.main import app
from app.core import config

# Anything from SERVER_NAME
use_domain = config.SERVER_NAME.replace(".", r"\.")
cors_origins_regex = re.compile(
r"^(https?:\/\/(?:.+\.)?(" + use_domain + r")(?::\d{1,5})?)$"
)
CORS(app, origins=cors_origins_regex, supports_credentials=True)
origins = []

print(config.BACKEND_CORS_ORIGINS)

# Set all CORS enabled origins
if config.BACKEND_CORS_ORIGINS:
origins_raw = config.BACKEND_CORS_ORIGINS.split(",")
for origin in origins_raw:
use_origin = origin.strip()
origins.append(use_origin)

CORS(app, origins=origins, supports_credentials=True)
2 changes: 2 additions & 0 deletions {{cookiecutter.project_slug}}/cookiecutter-config-file.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ default_context:
secret_key: '{{ cookiecutter.secret_key }}'
first_superuser: '{{ cookiecutter.first_superuser }}'
first_superuser_password: '{{ cookiecutter.first_superuser_password }}'
backend_cors_origins: '{{ cookiecutter.backend_cors_origins }}'
postgres_password: '{{ cookiecutter.postgres_password }}'
pgadmin_default_user: '{{ cookiecutter.pgadmin_default_user }}'
pgadmin_default_user_password: '{{ cookiecutter.pgadmin_default_user_password }}'
Expand All @@ -24,5 +25,6 @@ default_context:
docker_image_frontend: '{{ cookiecutter.docker_image_frontend }}'
_copy_without_render:
- frontend/src/**/*.html
- frontend/src/**/*.vue
- frontend/node_modules/*
_template: ./
2 changes: 1 addition & 1 deletion {{cookiecutter.project_slug}}/docker-compose.dev.ports.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ services:
proxy:
ports:
- '80:80'
- '8080:8080'
- '8090:8080'
flower:
ports:
- '5555:5555'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ services:
environment:
- SERVER_NAME=${DOMAIN}
- SECRET_KEY={{cookiecutter.secret_key}}
- BACKEND_CORS_ORIGINS=${BACKEND_CORS_ORIGINS}
- SENTRY_DSN={{cookiecutter.sentry_dsn}}
- POSTGRES_SERVER=${POSTGRES_SERVER}
- POSTGRES_USER=${POSTGRES_USER}
Expand Down
61 changes: 0 additions & 61 deletions {{cookiecutter.project_slug}}/frontend/.angular-cli.json

This file was deleted.

13 changes: 0 additions & 13 deletions {{cookiecutter.project_slug}}/frontend/.editorconfig

This file was deleted.

7 changes: 7 additions & 0 deletions {{cookiecutter.project_slug}}/frontend/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
VUE_APP_DOMAIN_DEV={{cookiecutter.domain_dev}}
VUE_APP_DOMAIN_STAG={{cookiecutter.domain_staging}}
VUE_APP_DOMAIN_PROD={{cookiecutter.domain_main}}
VUE_APP_NAME='{{cookiecutter.project_name}}'
VUE_APP_ENV=development
# VUE_APP_ENV=staging
# VUE_APP_ENV=production
Loading