Skip to content

Commit

Permalink
update readme
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisjsimpson committed Jan 3, 2025
1 parent 133c037 commit 760b67e
Showing 1 changed file with 76 additions and 75 deletions.
151 changes: 76 additions & 75 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 📹 😎

Expand All @@ -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.

Expand All @@ -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
```

Expand All @@ -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).

Expand All @@ -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:
Expand All @@ -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
Expand All @@ -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)

Expand All @@ -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

Expand All @@ -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

Expand All @@ -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
Expand All @@ -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:

Expand Down Expand Up @@ -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
Expand All @@ -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

Expand All @@ -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)

Expand All @@ -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

Expand All @@ -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>
Expand All @@ -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

Expand All @@ -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:

Expand All @@ -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
Expand All @@ -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
Expand Down

0 comments on commit 760b67e

Please sign in to comment.