Skip to content

91 generate settings yaml #94

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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -38,6 +38,7 @@ SERVER_NAME=${SERVER_NAME}
#SESSION_COOKIE_SECURE=True
#SESSION_COOKIE_HTTPONLY=True
#SESSION_COOKIE_SAMESITE=None
PERMANENT_SESSION_LIFETIME="1800"

MAIL_DEFAULT_SENDER=${MAIL_DEFAULT_SENDER}

@@ -77,6 +78,8 @@ SUBSCRIBIE_DOMAIN="subscriby.shop"
PRIVATE_KEY="/tmp/private.pem"
PUBLIC_KEY="/tmp/public.pem"

ANTI_SPAM_SHOP_NAMES_MODEL_FULL_PATH="/change/me"


# Optional
TELEGRAM_TOKEN=${TELEGRAM_TOKEN}
216 changes: 178 additions & 38 deletions main.py
Original file line number Diff line number Diff line change
@@ -3,7 +3,6 @@
import errno
import shutil
import re
import subprocess
from werkzeug.security import generate_password_hash
import sqlite3
import datetime
@@ -16,10 +15,111 @@
from starlette.routing import Route
from starlette.responses import JSONResponse
from starlette.responses import PlainTextResponse
from strictyaml import (
load,
as_document,
Map,
Str,
Bool,
Url,
Regex,
Int,
Email,
CommaSeparated,
YAMLError,
)

load_dotenv(verbose=True)
logging.basicConfig(level="DEBUG")

"""
Generate a valid shop (vassal) settings
against strictyaml schema fro subscribie.settings schema

Example post request:

curl 'http://127.0.0.1:8002' -d '{"company": {"name": "ACME Corp"}, "users": ["fred@example.com"], "password": "changeme", "plans": [{"title": "Soap", "description": "Best soap ever", "interval_amount": 5000, "interval_unit": "monthly", "sell_price": 1000}], "login_token": "changeme"}' # noqa: E501

gist:
.. build new settings dict
settings = as_document(settings)
# Load and validate against schema
load(settings.as_yaml(), schema)
"""

# TODO package subscribie properly and import from subscribie.settings,
# adding subscribie as a dependency
schema = Map(
{
"FLASK_ENV": Str(),
"SENTRY_SDK_DSN": Str(),
"SENTRY_SDK_SESSION_REPLAY_ID": Str(),
"SAAS_URL": Url(),
"SAAS_API_KEY": Str(),
"SAAS_ACTIVATE_ACCOUNT_PATH": Str(),
"SUBSCRIBIE_REPO_DIRECTORY": Str(),
"SQLALCHEMY_TRACK_MODIFICATIONS": Bool(),
"SQLALCHEMY_DATABASE_URI": Regex("sqlite:////.*"),
"SECRET_KEY": Str(),
"DB_FULL_PATH": Str(),
"MODULES_PATH": Str(),
"TEMPLATE_BASE_DIR": Str(),
"THEME_NAME": Str(),
"CUSTOM_PAGES_PATH": Str(),
"UPLOADED_IMAGES_DEST": Str(),
"UPLOADED_FILES_DEST": Str(),
"MAX_CONTENT_LENGTH": Str(),
"SUCCESS_REDIRECT_URL": Str(),
"THANKYOU_URL": Str(),
"EMAIL_LOGIN_FROM": Str(),
"EMAIL_QUEUE_FOLDER": Str(),
"SERVER_NAME": Str(),
"PERMANENT_SESSION_LIFETIME": Int(),
"MAIL_DEFAULT_SENDER": Email(),
"STRIPE_LIVE_PUBLISHABLE_KEY": Regex("pk_live_..*"),
"STRIPE_LIVE_SECRET_KEY": Regex("sk_live_..*"),
"STRIPE_TEST_PUBLISHABLE_KEY": Regex("pk_test_..*"),
"STRIPE_TEST_SECRET_KEY": Regex("sk_test_..*"),
"STRIPE_CONNECT_ACCOUNT_ANNOUNCER_HOST": Url(),
"PYTHON_LOG_LEVEL": Str(),
"PLAYWRIGHT_HOST": Url(),
"PLAYWRIGHT_HEADLESS": Bool(),
"PATH_TO_SITES": Str(),
"PATH_TO_RENAME_SCRIPT": Str(),
"SUBSCRIBIE_DOMAIN": Str(),
"PRIVATE_KEY": Str(),
"PUBLIC_KEY": Str(),
"SUPPORTED_CURRENCIES": CommaSeparated(Str()),
"ANTI_SPAM_SHOP_NAMES_MODEL_FULL_PATH": Str(),
"TELEGRAM_TOKEN": Str(),
"TELEGRAM_CHAT_ID": Str(),
"TELEGRAM_PYTHON_LOG_LEVEL": Str(),
"TEST_SHOP_OWNER_EMAIL_ISSUE_704": Email(),
"TEST_SUBSCRIBER_EMAIL_USER": Email(),
"TEST_SHOP_OWNER_LOGIN_URL": Url(),
"EMAIL_SEARCH_API_HOST": Str(), # Example: "email-search-api.example.com"
"IMAP_SEARCH_UNSEEN": Str(), # Example: "1"
"IMAP_SEARCH_SINCE_DATE": Str(), # Example: "21-Aug-2024"
"PLAYWRIGHT_HEADLESS": Bool(),
"PLAYWRIGHT_SLOWMO": Int(),
"PLAYWRIGHT_MAX_RETRIES": Int(),
"RESET_PASSWORD_IMAP_SEARCH_SUBJECT": Str(), # Example: "Password Reset"
"SHOP_OWNER_EMAIL_HOST": Str(),
"SHOP_OWNER_EMAIL_USER": Email(),
"SHOP_OWNER_MAGIC_LOGIN_IMAP_SEARCH_SUBJECT": Str(), # "Subscribie Magic Login"
"SHOP_OWNER_EMAIL_PASSWORD": Str(),
"SUBSCRIBER_EMAIL_HOST": Str(),
"SUBSCRIBER_EMAIL_USER": Email(),
"SUBSCRIBER_EMAIL_PASSWORD": Str(),
}
)


# Load application settings according to schema

# Schema for Subscribie application settings
# See also https://hitchdev.com/strictyaml/


class EnvSettings(dict):
def __setitem__(self, key, value):
@@ -129,35 +229,29 @@ async def deploy(request):
if e.errno != errno.EEXIST:
raise
try:
# Create .env file from .env.example
envFileSrc = Path(
os.getenv("SUBSCRIBIE_REPO_DIRECTORY") + "/.envsubst.template"
) # noqa E501
logging.debug(f"envFileSrc is: {envFileSrc}")

envFileDst = Path(dstDir + "/.env")
logging.debug(f"envFileDst is: {envFileDst}")
shutil.copy(envFileSrc, envFileDst)
# Create settings.yaml file
settingsYAMLFile = Path(dstDir + "/settings.yaml")
logging.debug(f"settingsYAMLFile is: {settingsYAMLFile}")
# Build envSettings vars
envSettings = EnvSettings()
envSettings["FLASK_ENV"] = os.getenv("FLASK_ENV")
envSettings["PERMANENT_SESSION_LIFETIME"] = os.getenv(
"PERMANENT_SESSION_LIFETIME"
)
envSettings["SENTRY_SDK_DSN"] = os.getenv(
"SENTRY_SDK_DSN"
)
envSettings["SENTRY_SDK_SESSION_REPLAY_ID"] = os.getenv(
"SENTRY_SDK_SESSION_REPLAY_ID"
)
envSettings["PERMANENT_SESSION_LIFETIME"] = os.getenv(
"PERMANENT_SESSION_LIFETIME"
)
envSettings[
"SUBSCRIBIE_REPO_DIRECTORY"
] = f"{os.getenv('SUBSCRIBIE_REPO_DIRECTORY')}"

envSettings["SERVER_NAME"] = webaddress

custom_pages_path = Path(dstDir + "/custom_pages/")
envSettings["CUSTOM_PAGES_PATH"] = custom_pages_path
envSettings["CUSTOM_PAGES_PATH"] = str(custom_pages_path)

if Path(custom_pages_path).exists() is False:
os.mkdir(custom_pages_path)
@@ -167,6 +261,9 @@ async def deploy(request):
] = f"{Path(os.getenv('SUBSCRIBIE_REPO_DIRECTORY'))}/subscribie/themes/" # noqa: E501

envSettings["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///{dstDir}data.db"
envSettings["SQLALCHEMY_TRACK_MODIFICATIONS"] = os.getenv(
"SQLALCHEMY_TRACK_MODIFICATIONS"
) # noqa: E501
envSettings["DB_FULL_PATH"] = f"{dstDir}data.db"

envSettings[
@@ -200,8 +297,8 @@ async def deploy(request):
uploadedFilesDst = Path(dstDir + "/uploads/")
os.makedirs(uploadedFilesDst, exist_ok=True)

envSettings["UPLOADED_IMAGES_DEST"] = uploadImgDst
envSettings["UPLOADED_FILES_DEST"] = uploadedFilesDst
envSettings["UPLOADED_IMAGES_DEST"] = str(uploadImgDst)
envSettings["UPLOADED_FILES_DEST"] = str(uploadedFilesDst)

successRedirectUrl = "https://" + webaddress + "/complete_mandate"
envSettings["SUCCESS_REDIRECT_URL"] = successRedirectUrl
@@ -236,13 +333,56 @@ async def deploy(request):
"SUPPORTED_CURRENCIES"
) # noqa: E501

envVars = "\n".join(map(str, envSettings))
my_env = {**os.environ.copy(), **envSettings} # Merge dicts
subprocess.run(
f"export $(xargs <{envVars}; cat {envFileSrc} | envsubst > {dstDir}.env)", # noqa: E501
shell=True,
env=my_env,
)
envSettings["ANTI_SPAM_SHOP_NAMES_MODEL_FULL_PATH"] = os.getenv(
"ANTI_SPAM_SHOP_NAMES_MODEL_FULL_PATH"
) # noqa: E501

envSettings["MAX_CONTENT_LENGTH"] = os.getenv(
"MAX_CONTENT_LENGTH"
) # noqa: E501

envSettings["MODULES_PATH"] = os.getenv("MODULES_PATH") # noqa: E501

envSettings["PLAYWRIGHT_HEADLESS"] = os.getenv(
"PLAYWRIGHT_HEADLESS"
) # noqa: E501

envSettings["PLAYWRIGHT_HOST"] = os.getenv("PLAYWRIGHT_HOST") # noqa: E501

envSettings["PUBLIC_KEY"] = os.getenv("PUBLIC_KEY") # noqa: E501

envSettings["PRIVATE_KEY"] = os.getenv("PRIVATE_KEY") # noqa: E501
envSettings["PYTHON_LOG_LEVEL"] = os.getenv("PYTHON_LOG_LEVEL") # noqa: E501

envSettings["SECRET_KEY"] = os.getenv("SECRET_KEY") # noqa: E501
envSettings["SUBSCRIBIE_DOMAIN"] = os.getenv("SUBSCRIBIE_DOMAIN") # noqa: E501
envSettings["TEST_SHOP_OWNER_EMAIL_ISSUE_704"] = os.getenv(
"TEST_SHOP_OWNER_EMAIL_ISSUE_704"
) # noqa: E501
envSettings["TEST_SHOP_OWNER_LOGIN_URL"] = os.getenv(
"TEST_SHOP_OWNER_LOGIN_URL"
) # noqa: E501

envSettings["THEME_NAME"] = os.getenv("THEME_NAME") # noqa: e501
additional_settings = ['EMAIL_SEARCH_API_HOST', 'IMAP_SEARCH_SINCE_DATE', 'IMAP_SEARCH_UNSEEN', 'PLAYWRIGHT_MAX_RETRIES', 'PLAYWRIGHT_SLOWMO', 'RESET_PASSWORD_IMAP_SEARCH_SUBJECT', 'SHOP_OWNER_EMAIL_HOST', 'SHOP_OWNER_EMAIL_PASSWORD', 'SHOP_OWNER_EMAIL_USER', 'SHOP_OWNER_MAGIC_LOGIN_IMAP_SEARCH_SUBJECT', 'SUBSCRIBER_EMAIL_HOST', 'SUBSCRIBER_EMAIL_PASSWORD', 'SUBSCRIBER_EMAIL_USER', 'TEST_SUBSCRIBER_EMAIL_USER']

for additional_setting in additional_settings:
if os.getenv(additional_setting) == 'None':
print(f"{additional_setting} is None. exiting")
envSettings[additional_setting] = os.getenv(additional_setting) # noqa: E501



newShopSettings = as_document(envSettings)
# Attempt to validate new Shop schema
try:
shopSettings = load(newShopSettings.as_yaml(), schema)
print("shopSettings validated")
except YAMLError as error:
logging.error(error)
exit(1)
with open(settingsYAMLFile, "w") as fp:
fp.write(shopSettings.as_yaml())

except KeyError as e:
print(f"KeyError missing config? {e}")
@@ -284,7 +424,7 @@ async def deploy(request):
cur.execute("INSERT INTO payment_provider (stripe_active) VALUES(0)") # noqa: E501
# Set default_currency
cur.execute(
"INSERT INTO setting (default_currency, default_country_code) VALUES (?,?)",
"INSERT INTO setting (default_currency, default_country_code) VALUES (?,?)", # noqa: E501
(
default_currency,
default_country_code,
@@ -324,15 +464,15 @@ async def deploy(request):

cur.execute(
"""INSERT INTO plan
(created_at, archived, uuid,
title,
description,
sell_price,
interval_amount,
interval_unit,
trial_period_days,
private)
VALUES (?,?,?,?,?,?,?,?,?,?)""",
(created_at, archived, uuid,
title,
description,
sell_price,
interval_amount,
interval_unit,
trial_period_days,
private)
VALUES (?,?,?,?,?,?,?,?,?,?)""",
(
now,
archived,
@@ -360,9 +500,9 @@ async def deploy(request):
# Item requirements
cur.execute(
"""INSERT INTO plan_requirements (id , created_at, plan_id,
instant_payment, subscription)
VALUES ( 1, ?, 1, ?, ?)
""",
instant_payment, subscription)
VALUES ( 1, ?, 1, ?, ?)
""",
(now, requires_instant_payment, requires_subscription),
)

@@ -372,8 +512,8 @@ async def deploy(request):

cur.executemany(
"""INSERT INTO plan_selling_points
(id, created_at, point, plan_id)
VALUES (?, ?, ?, ?)""",
(id, created_at, point, plan_id)
VALUES (?, ?, ?, ?)""",
points,
)
con.commit()
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -2,3 +2,4 @@ python-dotenv==0.21.0
starlette==0.21.0
uvicorn==0.19.0
Werkzeug==2.2.2
strictyaml