From ece83fe759d10b0637643a47b77aa7c332fc592f Mon Sep 17 00:00:00 2001 From: chrisjsimpson Date: Thu, 25 Jan 2024 23:56:09 +0000 Subject: [PATCH 1/5] wip Fix #91 generate settings.yaml --- .env.example | 3 + main.py | 179 ++++++++++++++++++++++++++++++++++++++--------- requirements.txt | 1 + 3 files changed, 148 insertions(+), 35 deletions(-) diff --git a/.env.example b/.env.example index 5d0dcb8..f6e594a 100644 --- a/.env.example +++ b/.env.example @@ -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} diff --git a/main.py b/main.py index ab53926..4692ce4 100644 --- a/main.py +++ b/main.py @@ -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,93 @@ 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, +) 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(), + "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_SHOP_OWNER_LOGIN_URL": Url(), + } +) + + +# 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,15 +211,9 @@ 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") @@ -151,7 +227,7 @@ async def deploy(request): 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) @@ -161,6 +237,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[ @@ -194,8 +273,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 @@ -230,13 +309,43 @@ 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 + + newShopSettings = as_document(envSettings) + # Attempt to validate new Shop schema + shopSettings = load(newShopSettings.as_yaml(), schema) + with open(settingsYAMLFile, "w") as fp: + fp.write(shopSettings.as_yaml()) except KeyError as e: print(f"KeyError missing config? {e}") @@ -278,7 +387,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, @@ -318,15 +427,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, @@ -354,9 +463,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), ) @@ -366,8 +475,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() diff --git a/requirements.txt b/requirements.txt index a48be91..1791399 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ python-dotenv==0.21.0 starlette==0.21.0 uvicorn==0.19.0 Werkzeug==2.2.2 +strictyaml From d7b3df2a389eba8b2ae8f45380e270ddfa421338 Mon Sep 17 00:00:00 2001 From: chrisjsimpson Date: Sat, 27 Jan 2024 16:40:25 +0000 Subject: [PATCH 2/5] wip Fix #91 generate settings yaml --- main.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index 4692ce4..a2342ff 100644 --- a/main.py +++ b/main.py @@ -26,6 +26,7 @@ Int, Email, CommaSeparated, + YAMLError, ) load_dotenv(verbose=True) @@ -343,7 +344,11 @@ async def deploy(request): newShopSettings = as_document(envSettings) # Attempt to validate new Shop schema - shopSettings = load(newShopSettings.as_yaml(), schema) + try: + shopSettings = load(newShopSettings.as_yaml(), schema) + except YAMLError as error: + logging.error(error) + exit(1) with open(settingsYAMLFile, "w") as fp: fp.write(shopSettings.as_yaml()) From 3b74420a8a819da9efcd290f1e5b932271e316b2 Mon Sep 17 00:00:00 2001 From: chrisjsimpson Date: Thu, 25 Jan 2024 23:56:09 +0000 Subject: [PATCH 3/5] wip Fix #91 generate settings.yaml --- .env.example | 3 + main.py | 179 ++++++++++++++++++++++++++++++++++++++--------- requirements.txt | 1 + 3 files changed, 148 insertions(+), 35 deletions(-) diff --git a/.env.example b/.env.example index 5d0dcb8..f6e594a 100644 --- a/.env.example +++ b/.env.example @@ -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} diff --git a/main.py b/main.py index 93eac5e..e03bddf 100644 --- a/main.py +++ b/main.py @@ -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,93 @@ 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, +) 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(), + "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_SHOP_OWNER_LOGIN_URL": Url(), + } +) + + +# 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,15 +211,9 @@ 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") @@ -157,7 +233,7 @@ async def deploy(request): 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 +243,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 +279,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 +315,43 @@ 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 + + newShopSettings = as_document(envSettings) + # Attempt to validate new Shop schema + shopSettings = load(newShopSettings.as_yaml(), schema) + with open(settingsYAMLFile, "w") as fp: + fp.write(shopSettings.as_yaml()) except KeyError as e: print(f"KeyError missing config? {e}") @@ -284,7 +393,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 +433,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 +469,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 +481,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() diff --git a/requirements.txt b/requirements.txt index a48be91..1791399 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ python-dotenv==0.21.0 starlette==0.21.0 uvicorn==0.19.0 Werkzeug==2.2.2 +strictyaml From 0570ed88d21cad68d8f2320a1e5f40466e549ed6 Mon Sep 17 00:00:00 2001 From: chrisjsimpson Date: Sat, 27 Jan 2024 16:40:25 +0000 Subject: [PATCH 4/5] wip Fix #91 generate settings yaml --- main.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/main.py b/main.py index e03bddf..9c9eb1a 100644 --- a/main.py +++ b/main.py @@ -26,6 +26,7 @@ Int, Email, CommaSeparated, + YAMLError, ) load_dotenv(verbose=True) @@ -349,7 +350,11 @@ async def deploy(request): newShopSettings = as_document(envSettings) # Attempt to validate new Shop schema - shopSettings = load(newShopSettings.as_yaml(), schema) + try: + shopSettings = load(newShopSettings.as_yaml(), schema) + except YAMLError as error: + logging.error(error) + exit(1) with open(settingsYAMLFile, "w") as fp: fp.write(shopSettings.as_yaml()) From d5d7c47aa3372586a29041b68b7699bf1ee77516 Mon Sep 17 00:00:00 2001 From: chrisjsimpson Date: Tue, 25 Mar 2025 11:10:45 +0000 Subject: [PATCH 5/5] #91 add required schema settings to validate new shop schema --- main.py | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/main.py b/main.py index 9c9eb1a..f1e6c4f 100644 --- a/main.py +++ b/main.py @@ -52,6 +52,8 @@ 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(), @@ -93,7 +95,22 @@ "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(), } ) @@ -218,15 +235,15 @@ async def deploy(request): # 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')}" @@ -346,12 +363,21 @@ async def deploy(request): "TEST_SHOP_OWNER_LOGIN_URL" ) # noqa: E501 - envSettings["THEME_NAME"] = os.getenv("THEME_NAME") # 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)