-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
133c037
commit 760b67e
Showing
1 changed file
with
76 additions
and
75 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,7 +13,7 @@ https://user-images.githubusercontent.com/1718624/196006904-3f00f852-3b86-4ecc-b | |
[![Update All Sites](https://github.com/Subscribie/subscribie/actions/workflows/update-all-sites.yml/badge.svg)](https://github.com/Subscribie/subscribie/actions/workflows/update-all-sites.yml) | ||
[![Update Onboarding Site](https://github.com/Subscribie/subscribie/actions/workflows/update-onboarding-site.yml/badge.svg)](https://github.com/Subscribie/subscribie/actions/workflows/update-onboarding-site.yml) | ||
[![testing stripe prod and test webhooks](https://github.com/Subscribie/subscribie/actions/workflows/testing-stripe-prod-and-test-webhooks.yml/badge.svg)](https://github.com/Subscribie/subscribie/actions/workflows/testing-stripe-prod-and-test-webhooks.yml) | ||
[![Verify Prod Onbording](https://github.com/Subscribie/subscribie/actions/workflows/prod-test-onboarding-works.yml/badge.svg)](https://github.com/Subscribie/subscribie/actions/workflows/prod-test-onboarding-works.yml) | ||
[![Verify Prod Onboarding](https://github.com/Subscribie/subscribie/actions/workflows/prod-test-onboarding-works.yml/badge.svg)](https://github.com/Subscribie/subscribie/actions/workflows/prod-test-onboarding-works.yml) | ||
[![Login Emails Sending](https://github.com/Subscribie/subscribie/actions/workflows/test-email-shopowner-704.yml/badge.svg)](https://github.com/Subscribie/subscribie/actions/workflows/test-email-shopowner-704.yml) | ||
[![Release](https://github.com/Subscribie/subscribie/actions/workflows/release.yml/badge.svg)](https://github.com/Subscribie/subscribie/actions/workflows/release.yml) | ||
[![Blog](https://github.com/Subscribie/subscribie/actions/workflows/check-online-status.yml/badge.svg)](https://github.com/Subscribie/subscribie/actions/workflows/check-online-status.yml) | ||
|
@@ -49,7 +49,7 @@ https://user-images.githubusercontent.com/1718624/196006904-3f00f852-3b86-4ecc-b | |
[https://footballclub.subscriby.shop/](https://footballclub.subscriby.shop/) | ||
|
||
<a href="https://footballclub.subscriby.shop/" target="_blank"> | ||
<img src="https://user-images.githubusercontent.com/1718624/157171840-6d19fdea-397d-4686-b812-a80f0e15f81e.png" /></a> | ||
<img src="https://user-images.githubusercontent.com/1718624/157171840-6d19fdea-397d-4686-b812-a80f0e15f81e.png" alt="Example subscription sign up webpage for a local football club to offer two subscriptions plans for their club memberships" /></a> | ||
|
||
## Video Demos 📹 😎 | ||
|
||
|
@@ -73,11 +73,11 @@ Quickly build a subscription based website, taking weekly/monthly/yearly payment | |
|
||
Don't want/know how to code? Pay for the hosted service. | ||
|
||
https://subscribie.co.uk | ||
[https://subscribie.co.uk](https://subscribie.co.uk) | ||
|
||
## Why is this project useful? | ||
|
||
A lot of the hard work has been done for you. If you're a devloper, you can | ||
A lot of the hard work has been done for you. If you're a developer, you can | ||
impress your clients quickly, if you're a small business owner, you might want | ||
to try the [subscription website hosting service](http://subscriptionwebsitebuilder.co.uk) but you can always host it yourself too. | ||
|
||
|
@@ -88,10 +88,12 @@ to try the [subscription website hosting service](http://subscriptionwebsitebuil | |
- Uses Stripe for subscriptions & one-off payments | ||
|
||
## Developer Quickstart | ||
|
||
Quickly run Subscribie from a container: | ||
|
||
If you use `podman`: | ||
``` | ||
|
||
```shell | ||
podman run -p 8082:80 ghcr.io/subscribie/subscribie/subscribie:latest | ||
``` | ||
|
||
|
@@ -101,11 +103,11 @@ Or, if you prefer Docker: | |
docker run -p 8082:80 ghcr.io/subscribie/subscribie/subscribie:latest | ||
``` | ||
|
||
Then visit: http://127.0.0.1:8082/auth/login | ||
Then visit: [http://127.0.0.1:8082/auth/login](http://127.0.0.1:8082/auth/login) | ||
|
||
Username: [email protected] | ||
Username: `[email protected]` | ||
|
||
Password: password | ||
Password: `password` | ||
|
||
[More about containers](https://mkdev.me/en/posts/the-tool-that-really-runs-your-containers-deep-dive-into-runc-and-oci-specifications). | ||
|
||
|
@@ -120,20 +122,20 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) and quickstart below. | |
```shell | ||
git clone https://github.com/Subscribie/subscribie.git | ||
cd subscribie | ||
cp .env.example .env # Copy default .env settings (read it) | ||
# Read the .env file so you're familiar with the env variables | ||
cp settings.yaml.example settings.yaml # Copy default settings to settings.yaml (read it) | ||
# Read the .settings.yam file so you're familiar with the application settings/variables | ||
``` | ||
|
||
(Optional) Set database path. Edit `.env` and set `DB_FULL_PATH` and `SQLALCHEMY_DATABASE_URI`. (optional but recommended- do not store data.db in /tmp). | ||
(Optional) Set database path. Edit `settings.yaml` and set `DB_FULL_PATH` and `SQLALCHEMY_DATABASE_URI`. (optional but recommended- do not store data.db in /tmp). | ||
|
||
> Notice that `sqlite:///` starts with *three* forward slashes. So, if you want to store the database in `/home/sam/data.db` then, | ||
you should put `sqlite:////home/sam/data.db` (note four `/`'s) | ||
|
||
```shell | ||
# Open the .env file, and change the database path to store somewhere else (e.g. your `/home/Documents/data.db` folder): | ||
```yaml | ||
# Open the settings.yaml file, and change the database path to store somewhere else (e.g. your `/home/Documents/data.db` folder): | ||
|
||
DB_FULL_PATH="/tmp/data.db" | ||
SQLALCHEMY_DATABASE_URI="sqlite:////tmp/data.db" | ||
DB_FULL_PATH: /tmp/data.db | ||
SQLALCHEMY_DATABASE_URI: sqlite:////tmp/data.db | ||
``` | ||
Create python environment and run flask: | ||
|
@@ -157,14 +159,15 @@ The database file is called `data.db`. Note, | |
You need a Stripe api key. | ||
|
||
1. Create a stripe account | ||
2. Go to api keys https://dashboard.stripe.com/test/apikeys (test mode) | ||
2. Go to api keys [https://dashboard.stripe.com/test/apikeys](https://dashboard.stripe.com/test/apikeys) (test mode) | ||
3. Copy `Publishable key` and `Secret key` | ||
4. Paste the keys into your `.env` file: | ||
4. Paste the keys into your `settings.yaml` file: | ||
|
||
Edit your .env file | ||
``` | ||
STRIPE_TEST_PUBLISHABLE_KEY=pk_test_<your-Publishable-key> | ||
STRIPE_TEST_SECRET_KEY=sk_test_<your-Secret-key> | ||
Edit your `settings.yaml` file | ||
|
||
```yaml | ||
STRIPE_TEST_PUBLISHABLE_KEY: pk_test_<your-Publishable-key> | ||
STRIPE_TEST_SECRET_KEY: sk_test_<your-Secret-key> | ||
``` | ||
## Start Subscribie | ||
|
@@ -175,7 +178,7 @@ export FLASK_DEBUG=1 | |
flask run | ||
``` | ||
|
||
Now visit http://127.0.0.1:5000 | ||
Now visit [http://127.0.0.1:5000](http://127.0.0.1:5000) | ||
|
||
## Quickstart (with Docker) | ||
|
||
|
@@ -184,7 +187,7 @@ If you like to use docker-compose workflow for local development: | |
```shell | ||
git clone https://github.com/Subscribie/subscribie.git | ||
cd subscribie | ||
cp .env.example .env | ||
cp settings.yaml.example settings.yaml | ||
export COMPOSE_DOCKER_CLI_BUILD=1 | ||
export DOCKER_BUILDKIT=1 | ||
|
||
|
@@ -194,70 +197,74 @@ docker-compose up | |
# Wait for it to build... | ||
``` | ||
|
||
Then visit http://127.0.0.1:5000 | ||
Then visit [http://127.0.0.1:5000](http://127.0.0.1:5000) | ||
|
||
To go inside the container, you can do: `docker-compose exec web /bin/bash` | ||
To go inside the container, you can do: `docker-compose exec web /bin/bash` | ||
from the project root directory. | ||
|
||
# Logging & Debugging - How to change the logLevel | ||
Quick: edit your `.env` file and set `PYTHON_LOG_LEVEL=DEBUG`. | ||
## Logging & Debugging - How to change the logLevel | ||
|
||
Quick: edit your `settings.yaml` file and set: | ||
|
||
```yaml | ||
PYTHON_LOG_LEVEL: DEBUG | ||
``` | ||
E.g. to reduce the amount of logs, to `WARNING` or `CRITICAL`. | ||
|
||
The default log level is `DEBUG` which means show as much logging | ||
information as possible. | ||
|
||
The possible values are DEBUG, INFO, WARNING, ERROR, CRITICAL | ||
See https://docs.python.org/3/howto/logging.html | ||
See [https://docs.python.org/3/howto/logging.html](https://docs.python.org/3/howto/logging.html) | ||
|
||
Flask *does* need to be restarted for the log level to change. | ||
|
||
## How to change theme (theme development) | ||
|
||
### How to change from the default jesmond theme to the builder theme. | ||
### How to change from the default jesmond theme to the builder theme | ||
|
||
1. Edit your `.env` file | ||
1. Edit your `settings.yaml` file | ||
|
||
Change: | ||
Change: | ||
|
||
- `THEME_NAME="jesmond"` to `THEME_NAME="builder"` | ||
- **(optional)** change `TEMPLATE_BASE_DIR` if you want to store themes in a different directory. | ||
- `THEME_NAME="jesmond"` to `THEME_NAME="builder"` | ||
- **(optional)** change `TEMPLATE_BASE_DIR` if you want to store themes in a different directory. | ||
|
||
2. Stop & start subscribie | ||
|
||
3. Complete. The other theme will now load | ||
|
||
#### Create a new theme | ||
|
||
If you're creating a *new* theme, then change `TEMPLATE_BASE_DIR` to a directory **outside** of | ||
subscribie root project. | ||
|
||
If you're creating a *new* theme, then change `TEMPLATE_BASE_DIR` to a directory **outside** of subscribie root project. | ||
|
||
## API based authentication with jwt token | ||
|
||
#### Locally | ||
### Locally | ||
|
||
Locally you'll need to create public/private keys for secure | ||
jwt authentication. | ||
|
||
1) Generate public/private keys automatically | ||
1. Generate public/private keys automatically | ||
|
||
``` | ||
# Use the commands below to automaticaly create 'private.pem' file and key | ||
openssl genrsa -out private.pem 2048 | ||
# Use this command to automatically generate your public.pem | ||
openssl rsa -in private.pem -pubout > public.pem | ||
``` | ||
```shell | ||
# Use the commands below to automatically create 'private.pem' file and key | ||
openssl genrsa -out private.pem 2048 | ||
# Use this command to automatically generate your public.pem | ||
openssl rsa -in private.pem -pubout > public.pem | ||
``` | ||
|
||
2) Update .env file with PRIVATE_KEY and PUBLIC_KEY | ||
1. Update .env file with PRIVATE_KEY and PUBLIC_KEY | ||
|
||
```yaml | ||
PRIVATE_KEY: /path/to/private.pem | ||
PUBLIC_KEY: /path/to/public.pem | ||
``` | ||
|
||
``` | ||
PRIVATE_KEY="/path/to/private.pem" | ||
PUBLIC_KEY="/path/to/public.pem" | ||
``` | ||
## Logging in via jwt or basic auth | ||
|
||
Provide the username & password in a POST request, and a jwt token is returned for | ||
use in further requests. | ||
Provide the username & password in a POST request, and a jwt token is returned for use in further requests. | ||
|
||
## API Basics | ||
|
||
|
@@ -266,7 +273,8 @@ use in further requests. | |
```shell | ||
curl -v -d "[email protected]" -d "password=password" http://127.0.0.1:5000/auth/jwt-login | ||
``` | ||
## Http Basic auth login: | ||
|
||
## Http Basic auth login | ||
|
||
```shell | ||
curl -v --user "fred:password" http://127.0.0.1:5000/auth/jwt-login | ||
|
@@ -285,7 +293,7 @@ curl -v -H "Authorization: Bearer <token>" http://127.0.0.1:5000/auth/protected | |
curl -v -H "Content-Type: application/json" -H "Authorization: Bearer <token> " http://127.0.0.1:5000/api/plans | ||
``` | ||
|
||
## Create Plan | ||
## Create Plan | ||
|
||
Example POST request: | ||
|
||
|
@@ -352,8 +360,8 @@ The general steps are: | |
1. Update `model.py` with new field | ||
2. Run `flask db migrate -m 'added column title to plan model'` - which generates a new migration file | ||
The migration file will be in `./migrations/versions/<hash>_added_column_title_to.py` | ||
3. Update the migration , check it is correct (we generally *dont* add a `down` migration, see other migration files for an example). | ||
Warning: It is likley the generation migratino will have more/less infromatino than needed- edit it to be correct | ||
3. Update the migration , check it is correct (we generally *don't* add a `down` migration, see other migration files for an example). | ||
Warning: It is likely the generated migration will have more/less information than needed- edit it to be correct | ||
Note: We used to have to use `op.batch_alter_table` however that is no longer needed ([thanks sqlalchemy!](https://www.sqlalchemy.org/support.html)) | ||
4. Apply the migration to your local database: `flask db upgrade` | ||
5. Test (+write test), commit and push | ||
|
@@ -363,7 +371,7 @@ The general steps are: | |
1. New shop owner submits a form to create a new shop which hits `/start-building` endpoint | ||
2. Shop is created and a new shop is started (Shop owner sees *"Please wait"*) | ||
3. New Shop is ready | ||
4. Shop owner is automatically redirected to the new shop, loged in using automated one-time login | ||
4. Shop owner is automatically redirected to the new shop, logged in using automated one-time login | ||
|
||
### Saas Deployment | ||
|
||
|
@@ -378,7 +386,7 @@ Every shop owner gets a deployed flask application, with its own database. | |
If a Subscribie `shop` connects to Stripe (it does not have to), then the `shop` will announce it's [Stripe connect id](https://stripe.com/docs/connect/authentication#stripe-account-header) to the `stripe-connect-account-announcer`. | ||
|
||
The `stripe-connect-account-announcer` stores the Stripe connect id, so that when Stripe webhook events | ||
arrive, the `stripe-connect-webhook-endpoint-router` knows which `shop` to send the events to. | ||
arrive, the `stripe-connect-webhook-endpoint-router` knows which `shop` to send the events to. | ||
|
||
### [`stripe-connect-webhook-endpoint-router`](https://github.com/Subscribie/stripe-connect-webhook-endpoint-router) | ||
|
||
|
@@ -390,7 +398,7 @@ Receives [Stripe webhook events](https://stripe.com/docs/webhooks#webhooks-def), | |
3. Forwards the webhook event (e.g. [checkout-session-completed](stripe-connect-account-announcer)) to the correct Subscribie `shop` | ||
4. The `shop` [verifies the webhook from Stripe](https://stripe.com/docs/webhooks/signatures), and processes the event. | ||
|
||
> Note, in previous implementations there was one webhook endpoint per shop- this isn't compatible with Stripe when using Stripe Connect because there's a limmit on the number of webhooks, and connect events need to be routed based on their Stripe connect id anyway, hence the `stripe-connect-webhook-endpoint-router` performs this role. | ||
> Note, in previous implementations there was one webhook endpoint per shop- this isn't compatible with Stripe when using Stripe Connect because there's a limit on the number of webhooks, and connect events need to be routed based on their Stripe connect id anyway, hence the `stripe-connect-webhook-endpoint-router` performs this role. | ||
|
||
#### Failure modes | ||
|
||
|
@@ -400,15 +408,12 @@ If the `stripe-connect-account-announcer` suffers an outage, this means new shop | |
|
||
[uWSGI](https://uwsgi-docs.readthedocs.io/en/latest/) is used to run the application services. | ||
|
||
Subscribie Saas uses the following key compoent of uwsgi: [Emperor mode](https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html). | ||
Subscribie Saas uses the following key component of uwsgi: [Emperor mode](https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html). | ||
|
||
uWSGI **Emperor mode** starts and manages all running Subscribie shops as `uWSGI` vassals. | ||
|
||
> "If the emperor dies, all the vassals die."<br /> | ||
-[Emperor mode](https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html) | ||
|
||
<br /> | ||
|
||
> "If the emperor dies, all the vassals die." | ||
> -[Emperor mode](https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html) | ||
|
||
<details> | ||
<summary>uWSGI - Emperor</summary> | ||
|
@@ -419,27 +424,22 @@ uWSGI **Emperor mode** starts and manages all running Subscribie shops as `uWSGI | |
|
||
<details> | ||
<summary>uWSGI - vassal-template</summary> | ||
- A vassal template is injected into every new shop by the emporor. | ||
- A vassal template is injected into every new shop by the emperor. | ||
- This avoids having to copy and paste the same config for every new shop. | ||
- It also means vassal config is in one place. | ||
</details> | ||
<br /> | ||
<br /> | ||
|
||
|
||
### Systemd services | ||
|
||
<details> | ||
<summary>subscribie</summary> | ||
The uWSGI emperor and the vassals it sawns is defined as a single systemd service called `subscribie`. | ||
The uWSGI emperor and the vassals it spawns is defined as a single systemd service called `subscribie`. | ||
</details> | ||
|
||
<details> | ||
<summary>subscribie-deployer</summary> | ||
Responsible for listening for new Shop requests, and creating the Shop config which uWSGI needs to spawn a new Shop (aka uwsgi vassal). | ||
</details> | ||
<br /> | ||
<br /> | ||
|
||
### Optimisation | ||
|
||
|
@@ -451,9 +451,9 @@ and also socket-activated (note that's *two* different things): | |
*Result*: A reduction of > 17Gb of ram observed on a busy node. | ||
|
||
- OnDemandVassals: The application is not started until the first request is received. | ||
- Socket-activation: If running idle with no requests after x secconds, the shop is stoped- but is re-activated when a request comes in for the shop | ||
- Socket-activation: If running idle with no requests after x seconds, the shop is stopped- but is re-activated when a request comes in for the shop | ||
|
||
Socker activation is enabled by using the uWSGI feature `emperor-on-demand-extension = .socket` in the `emperor.ini` config. | ||
Socket activation is enabled by using the uWSGI feature `emperor-on-demand-extension = .socket` in the `emperor.ini` config. | ||
|
||
OnDemandVassals is enable by using the following config in the injected vassal config for every shop: | ||
|
||
|
@@ -473,10 +473,10 @@ Needed components / services. Check the `.env.example` for each of them. | |
- A [redis instance](https://github.com/Subscribie/redis), listening on localhost only (unless [protected with iptables](https://github.com/Subscribie/redis#ip-tables-config-example)) | ||
- A subscribie site with the [Builder module](https://github.com/Subscribie/module-builder) installed. The builder module submits new sites for building | ||
- [Subscribie deployer](https://github.com/Subscribie/subscribie-deployer) is an endpoint which listens for `POST` requests of new sites to be created. The [Builder module](https://github.com/Subscribie/module-builder) submits to this endpoint. The server requires [uwsgi](https://uwsgi-docs.readthedocs.io/en/latest/Install.html) to be installed. There is an example config in the [README](https://github.com/Subscribie/subscribie-deployer). | ||
- [Stripe connect account announcer](https://github.com/Subscribie/stripe-connect-account-announcer) Each shop announces its [stripe connect account id](https://stripe.com/docs/api/connected_accounts) to a redis endpoing (key is the account id, value is the shop url) | ||
- [Stripe connect account announcer](https://github.com/Subscribie/stripe-connect-account-announcer) Each shop announces its [stripe connect account id](https://stripe.com/docs/api/connected_accounts) to a redis endpoint (key is the account id, value is the shop url) | ||
- [Stripe webhook router](https://github.com/Subscribie/stripe-connect-webhook-endpoint-router) which routes webhooks to the correct shop | ||
|
||
#### Checklist | ||
#### Checklist | ||
|
||
- [ ] A Redis hostname is set | ||
- [ ] Redis is configured with password authentication | ||
|
@@ -492,6 +492,7 @@ Needed components / services. Check the `.env.example` for each of them. | |
## Docker help | ||
|
||
### How do I rebuild the container? | ||
|
||
Sometimes you need to rebuild the container if you've made changes to the `Dockerfile`. | ||
|
||
```shell | ||
|