diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..266af2a1 --- /dev/null +++ b/.env.example @@ -0,0 +1,83 @@ +HTTPS=off +DEBUG=true + +# This is not the real key used in prod +SECRET_KEY=(4sjxvhz@m5$0a$j0_pqicnc$s!vbve)z+&++m%g%bjhlz4+g2 + +DATABASE_URL=sqlite:///db.sqlite3 +# uncomment the next line if you want to use a postgres database +#DATABASE_URL=postgres://user:password@127.0.0.1:5432/sith +CACHE_URL=redis://127.0.0.1:6379/0 + +MEDIA_ROOT=data +STATIC_ROOT=static + +DEFAULT_FROM_EMAIL=bibou@git.an +SITH_COM_EMAIL=bibou_com@git.an + +HONEYPOT_VALUE=content +HONEYPOT_FIELD_NAME=body2 +HONEYPOT_FIELD_NAME_FORUM=message2 + +EMAIL_BACKEND=django.core.mail.backends.console.EmailBackend +EMAIL_HOST=localhost +EMAIL_PORT=25 + +SITH_URL=127.0.0.1:8000 +SITH_NAME="AE UTBM" + +SITH_MAIN_CLUB_ID=1 + +SITH_GROUP_ROOT_ID=1 +SITH_GROUP_PUBLIC_ID=2 +SITH_GROUP_SUBSCRIBERS_ID=3 +SITH_GROUP_OLD_SUBSCRIBERS_ID=4 +SITH_GROUP_ACCOUNTING_ADMIN_ID=5 +SITH_GROUP_COM_ADMIN_ID=6 +SITH_GROUP_COUNTER_ADMIN_ID=7 +SITH_GROUP_SAS_ADMIN_ID=8 +SITH_GROUP_FORUM_ADMIN_ID=9 +SITH_GROUP_PEDAGOGY_ADMIN_ID=10 + +SITH_GROUP_BANNED_ALCOHOL_ID=11 +SITH_GROUP_BANNED_COUNTER_ID=12 +SITH_GROUP_BANNED_SUBSCRIPTION_ID=13 + +SITH_CLUB_REFOUND_ID=89 +SITH_COUNTER_REFOUND_ID=38 +SITH_PRODUCT_REFOUND_ID=5 + +# Counter + +SITH_COUNTER_ACCOUNT_DUMP_ID=39 + +# Defines which product type is the refilling type, and thus increases the account amount +SITH_COUNTER_PRODUCTTYPE_REFILLING=3 + +SITH_ECOCUP_CONS=1152 +SITH_ECOCUP_DECO=1151 + +# Defines which product is the one year subscription and which one is the six month subscription +SITH_PRODUCT_SUBSCRIPTION_ONE_SEMESTER=1 +SITH_PRODUCT_SUBSCRIPTION_TWO_SEMESTERS=2 +SITH_PRODUCTTYPE_SUBSCRIPTION=2 + +# Defines which clubs let its members the ability to see users subscription history +SITH_CAN_CREATE_SUBSCRIPTION_HISTORY=1 +SITH_CAN_READ_SUBSCRIPTION_HISTORY=1 + +# SAS variables +SITH_SAS_ROOT_DIR_ID=4 + +# ET variables +SITH_EBOUTIC_CB_ENABLED=true +SITH_EBOUTIC_ET_URL="https://preprod-tpeweb.e-transactions.fr/cgi/MYchoix_pagepaiement.cgi" +SITH_EBOUTIC_PBX_SITE=1999888 +SITH_EBOUTIC_PBX_RANG=32 +SITH_EBOUTIC_PBX_IDENTIFIANT=2 +SITH_EBOUTIC_HMAC_KEY=0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF +SITH_EBOUTIC_PUB_KEY_PATH=sith/et_keys/pubkey.pem + +SITH_MAILING_FETCH_KEY=IloveMails +SENTRY_DSN= +SENTRY_ENV=production \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9a436879..5f7f5145 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,10 @@ on: branches: [master, taiste] workflow_dispatch: +env: + SECRET_KEY: notTheRealOne + DATABASE_URL: sqlite:///db.sqlite3 + jobs: pre-commit: name: Launch pre-commits checks (ruff) diff --git a/.gitignore b/.gitignore index cf8d7f67..19b65265 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ node_modules/ # compiled documentation site/ +.env diff --git a/docs/howto/prod.md b/docs/howto/prod.md index 769f681f..472f8c3e 100644 --- a/docs/howto/prod.md +++ b/docs/howto/prod.md @@ -2,13 +2,13 @@ Pour connecter l'application à une instance de sentry (ex: https://sentry.io), il est nécessaire de configurer la variable `SENTRY_DSN` -dans le fichier `settings_custom.py`. +dans le fichier `.env`. Cette variable est composée d'un lien complet vers votre projet sentry. ## Récupérer les statiques Nous utilisons du SCSS dans le projet. -En environnement de développement (`DEBUG=True`), +En environnement de développement (`DEBUG=true`), le SCSS est compilé à chaque fois que le fichier est demandé. Pour la production, le projet considère que chacun des fichiers est déjà compilé. diff --git a/docs/tutorial/install-advanced.md b/docs/tutorial/install-advanced.md index 318166ab..357d013c 100644 --- a/docs/tutorial/install-advanced.md +++ b/docs/tutorial/install-advanced.md @@ -47,19 +47,19 @@ Commencez par installer les dépendances système : === "Debian/Ubuntu" ```bash - sudo apt install postgresql redis libq-dev nginx + sudo apt install postgresql libq-dev nginx ``` === "Arch Linux" ```bash - sudo pacman -S postgresql redis nginx + sudo pacman -S postgresql nginx ``` === "macOS" ```bash - brew install postgresql redis lipbq nginx + brew install postgresql lipbq nginx export PATH="/usr/local/opt/libpq/bin:$PATH" source ~/.zshrc ``` @@ -77,34 +77,6 @@ uv sync --group prod C'est parce que ces dépendances compilent certains modules à l'installation. -## Configurer Redis - -Redis est utilisé comme cache. -Assurez-vous qu'il tourne : - -```bash -sudo systemctl redis status -``` - -Et s'il ne tourne pas, démarrez-le : - -```bash -sudo systemctl start redis -sudo systemctl enable redis # si vous voulez que redis démarre automatiquement au boot -``` - -Puis ajoutez le code suivant à la fin de votre fichier -`settings_custom.py` : - -```python -CACHES = { - "default": { - "BACKEND": "django.core.cache.backends.redis.RedisCache", - "LOCATION": "redis://127.0.0.1:6379", - } -} -``` - ## Configurer PostgreSQL PostgreSQL est utilisé comme base de données. @@ -139,26 +111,19 @@ en étant connecté en tant que postgres : psql -d sith -c "GRANT ALL PRIVILEGES ON SCHEMA public to sith"; ``` -Puis ajoutez le code suivant à la fin de votre -`settings_custom.py` : +Puis modifiez votre `.env`. +Dedans, décommentez l'url de la base de données +de postgres et commentez l'url de sqlite : -```python -DATABASES = { - "default": { - "ENGINE": "django.db.backends.postgresql", - "NAME": "sith", - "USER": "sith", - "PASSWORD": "password", - "HOST": "localhost", - "PORT": "", # laissez ce champ vide pour que le choix du port soit automatique - } -} +```dotenv +#DATABASE_URL=sqlite:///db.sqlite3 +DATABASE_URL=postgres://sith:password@localhost:5432/sith ``` Enfin, créez vos données : ```bash -uv run ./manage.py populate +uv run ./manage.py setup ``` !!! note @@ -247,7 +212,7 @@ Puis lancez ou relancez nginx : sudo systemctl restart nginx ``` -Dans votre `settings_custom.py`, remplacez `DEBUG=True` par `DEBUG=False`. +Dans votre `.env`, remplacez `DEBUG=true` par `DEBUG=false`. Enfin, démarrez le serveur Django : diff --git a/docs/tutorial/install.md b/docs/tutorial/install.md index bdd2cfc5..0a621587 100644 --- a/docs/tutorial/install.md +++ b/docs/tutorial/install.md @@ -7,6 +7,7 @@ Certaines dépendances sont nécessaires niveau système : - libjpeg - zlib1g-dev - gettext +- redis ### Installer WSL @@ -65,8 +66,8 @@ cd /mnt//vos/fichiers/comme/dhab ```bash sudo apt install curl build-essential libssl-dev \ - libjpeg-dev zlib1g-dev npm libffi-dev pkg-config \ - gettext git + libjpeg-dev zlib1g-dev npm libffi-dev pkg-config \ + gettext git redis curl -LsSf https://astral.sh/uv/install.sh | sh ``` @@ -75,7 +76,7 @@ cd /mnt//vos/fichiers/comme/dhab ```bash sudo pacman -Syu # on s'assure que les dépôts et le système sont à jour - sudo pacman -S uv gcc git gettext pkgconf npm + sudo pacman -S uv gcc git gettext pkgconf npm redis ``` === "macOS" @@ -84,7 +85,7 @@ cd /mnt//vos/fichiers/comme/dhab Il est également nécessaire d'avoir installé xcode ```bash - brew install git uv npm + brew install git uv npm redis # Pour bien configurer gettext brew link gettext # (suivez bien les instructions supplémentaires affichées) @@ -99,6 +100,15 @@ cd /mnt//vos/fichiers/comme/dhab Python ne fait pas parti des dépendances puisqu'il est automatiquement installé par uv. +Parmi les dépendances installées se trouve redis (que nous utilisons comme cache). +Redis est un service qui doit être activé pour être utilisé. +Pour cela, effectuez les commandes : + +```bash +sudo systemctl start redis +sudo systemctl enable redis # si vous voulez que redis démarre automatiquement au boot +``` + ## Finaliser l'installation Clonez le projet (depuis votre console WSL, si vous utilisez WSL) @@ -120,20 +130,24 @@ uv run ./manage.py install_xapian de texte à l'écran. C'est normal, il ne faut pas avoir peur. -Maintenant que les dépendances sont installées, nous -allons créer la base de données, la remplir avec des données de test, -et compiler les traductions. -Cependant, avant de faire cela, il est nécessaire de modifier -la configuration pour signifier que nous sommes en mode développement. -Pour cela, nous allons créer un fichier `sith/settings_custom.py` -et l'utiliser pour surcharger les settings de base. +Une fois les dépendances installées, il faut encore +mettre en place quelques éléments de configuration, +qui peuvent varier d'un environnement à l'autre. +Ces variables sont stockées dans un fichier `.env`. +Pour le créer, vous pouvez copier le fichier `.env.example` : ```bash -echo "DEBUG=True" > sith/settings_custom.py -echo 'SITH_URL = "localhost:8000"' >> sith/settings_custom.py +cp .env.example .env ``` -Enfin, nous pouvons lancer les commandes suivantes : +Les variables par défaut contenues dans le fichier `.env` +devraient convenir pour le développement, sans modification. + +Maintenant que les dépendances sont installées +et la configuration remplie, nous allons pouvoir générer +des données utiles pendant le développement. + +Pour cela, lancez les commandes suivantes : ```bash # Prépare la base de données @@ -171,6 +185,30 @@ uv run ./manage.py runserver [http://localhost:8000/api/docs](http://localhost:8000/api/docs), une interface swagger, avec toutes les routes de l'API. +!!! question "Pourquoi l'installation est aussi complexe ?" + + Cette question nous a été posée de nombreuses fois par des personnes + essayant d'installer le projet. + Il y a en effet un certain nombre d'étapes à suivre, + de paquets à installer et de commandes à exécuter. + + Le processus d'installation peut donc sembler complexe. + + En réalité, il est difficile de faire plus simple. + En effet, un site web a besoin de beaucoup de composants + pour être développé : il lui faut au minimum + une base de données, un cache, un bundler Javascript + et un interpréteur pour le code du serveur. + Pour nos besoin particuliers, nous utilisons également + un moteur de recherche full-text. + + Nous avons tenté au maximum de limiter le nombre de dépendances + et de sélecionner les plus simples à installer. + Cependant, il est impossible de retirer l'intégralité + de la complexité du processus. + Si vous rencontrez des difficulté lors de l'installation, + n'hésitez pas à demander de l'aide. + ## Générer la documentation La documentation est automatiquement mise en ligne à chaque envoi de code sur GitHub. diff --git a/docs/tutorial/structure.md b/docs/tutorial/structure.md index bc3fed36..aff331d2 100644 --- a/docs/tutorial/structure.md +++ b/docs/tutorial/structure.md @@ -72,12 +72,14 @@ sith/ ├── .gitattributes ├── .gitignore ├── .mailmap -├── manage.py (26) -├── mkdocs.yml (27) +├── .env (26) +├── .env.example (27) +├── manage.py (28) +├── mkdocs.yml (29) ├── uv.lock -├── pyproject.toml (28) -├── .venv/ (29) -├── .python-version (30) +├── pyproject.toml (30) +├── .venv/ (31) +├── .python-version (32) └── README.md ``` @@ -121,15 +123,19 @@ sith/ de manière transparente pour l'utilisateur. 24. Fichier de configuration de coverage. 25. Fichier de configuration de direnv. -26. Fichier généré automatiquement par Django. C'est lui +26. Contient les variables d'environnement, qui sont susceptibles + de varier d'une machine à l'autre. +27. Contient des valeurs par défaut pour le `.env` + pouvant convenir à un environnment de développement local +28. Fichier généré automatiquement par Django. C'est lui qui permet d'appeler des commandes de gestion du projet avec la syntaxe `python ./manage.py ` -27. Le fichier de configuration de la documentation, +29. Le fichier de configuration de la documentation, avec ses plugins et sa table des matières. -28. Le fichier où sont déclarés les dépendances et la configuration +30. Le fichier où sont déclarés les dépendances et la configuration de certaines d'entre elles. -29. Dossier d'environnement virtuel généré par uv -30. Fichier qui contrôle quel version de python utiliser pour le projet +31. Dossier d'environnement virtuel généré par uv +32. Fichier qui contrôle quelle version de python utiliser pour le projet ## L'application principale @@ -144,10 +150,9 @@ Il est organisé comme suit : ``` sith/ ├── settings.py (1) -├── settings_custom.py (2) -├── toolbar_debug.py (3) -├── urls.py (4) -└── wsgi.py (5) +├── toolbar_debug.py (2) +├── urls.py (3) +└── wsgi.py (4) ``` @@ -155,13 +160,10 @@ sith/ Ce fichier contient les paramètres de configuration du projet. Par exemple, il contient la liste des applications installées dans le projet. -2. Configuration maison pour votre environnement. - Toute variable que vous définissez dans ce fichier sera prioritaire - sur la configuration donnée dans `settings.py`. -3. Configuration de la barre de debug. +2. Configuration de la barre de debug. C'est inutilisé en prod, mais c'est très pratique en développement. -4. Fichier de configuration des urls du projet. -5. Fichier de configuration pour le serveur WSGI. +3. Fichier de configuration des urls du projet. +4. Fichier de configuration pour le serveur WSGI. WSGI est un protocole de communication entre le serveur et les applications. Ce fichier ne vous servira sans doute pas sur un environnement diff --git a/pyproject.toml b/pyproject.toml index 3e2cdf0f..9b0a5a81 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,6 +44,8 @@ dependencies = [ "django-honeypot<2.0.0,>=1.2.1", "pydantic-extra-types<3.0.0,>=2.10.1", "ical<9.0.0,>=8.3.0", + "redis[hiredis]<6.0.0,>=5.2.0", + "environs[django]<15.0.0,>=14.1.0", ] [project.urls] @@ -53,7 +55,6 @@ documentation = "https://sith-ae.readthedocs.io/" [dependency-groups] prod = [ "psycopg[c]<4.0.0,>=3.2.3", - "redis[hiredis]<6.0.0,>=5.2.0", ] dev = [ "django-debug-toolbar<5.0.0,>=4.4.6", diff --git a/sith/settings.py b/sith/settings.py index 42e46603..c8f7cf9e 100644 --- a/sith/settings.py +++ b/sith/settings.py @@ -34,7 +34,6 @@ https://docs.djangoproject.com/en/1.8/ref/settings/ """ import binascii -import logging import os import sys from datetime import timedelta @@ -43,25 +42,32 @@ from pathlib import Path import sentry_sdk from dateutil.relativedelta import relativedelta from django.utils.translation import gettext_lazy as _ +from environs import Env from sentry_sdk.integrations.django import DjangoIntegration from .honeypot import custom_honeypot_error -BASE_DIR = Path(__file__).parent.parent.resolve() +env = Env() +env.read_env() -os.environ["HTTPS"] = "off" +BASE_DIR = Path(__file__).parent.parent.resolve() # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = "(4sjxvhz@m5$0a$j0_pqicnc$s!vbve)z+&++m%g%bjhlz4+g2" +SECRET_KEY = env.str("SECRET_KEY") # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = False +DEBUG = env.bool("DEBUG", default=False) TESTING = "pytest" in sys.modules INTERNAL_IPS = ["127.0.0.1"] +# force csrf tokens and cookies to be secure when in https +CSRF_COOKIE_SECURE = env.bool("HTTPS", default=True) +SESSION_COOKIE_SECURE = env.bool("HTTPS", default=True) +X_FRAME_OPTIONS = "SAMEORIGIN" + ALLOWED_HOSTS = ["*"] # Application definition @@ -208,12 +214,12 @@ WSGI_APPLICATION = "sith.wsgi.application" # Database DATABASES = { - "default": { - "ENGINE": "django.db.backends.sqlite3", - "NAME": BASE_DIR / "db.sqlite3", - }, + "default": env.dj_db_url("DATABASE_URL", conn_max_age=None, conn_health_checks=True) } +if "CACHE_URL" in os.environ: + CACHES = {"default": env.dj_cache_url("CACHE_URL")} + SESSION_ENGINE = "django.contrib.sessions.backends.cached_db" # Logging @@ -265,13 +271,13 @@ PHONENUMBER_DEFAULT_REGION = "FR" # Medias MEDIA_URL = "/data/" -MEDIA_ROOT = BASE_DIR / "data" +MEDIA_ROOT = env.path("MEDIA_ROOT", default=BASE_DIR / "data") # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.8/howto/static-files/ STATIC_URL = "/static/" -STATIC_ROOT = BASE_DIR / "static" +STATIC_ROOT = env.path("STATIC_ROOT", default=BASE_DIR / "static") # Static files finders which allow to see static folder in all apps STATICFILES_FINDERS = [ @@ -295,24 +301,28 @@ AUTHENTICATION_BACKENDS = ["core.auth_backends.SithModelBackend"] LOGIN_URL = "/login" LOGOUT_URL = "/logout" LOGIN_REDIRECT_URL = "/" -DEFAULT_FROM_EMAIL = "bibou@git.an" -SITH_COM_EMAIL = "bibou_com@git.an" +DEFAULT_FROM_EMAIL = env.str("DEFAULT_FROM_EMAIL", default="bibou@git.an") +SITH_COM_EMAIL = env.str("SITH_COM_EMAIL", default="bibou_com@git.an") # Those values are to be changed in production to be more effective -HONEYPOT_FIELD_NAME = "body2" -HONEYPOT_VALUE = "content" +HONEYPOT_FIELD_NAME = env.str("HONEYPOT_FIELD_NAME", default="body2") +HONEYPOT_VALUE = env.str("HONEYPOT_VALUE", default="content") HONEYPOT_RESPONDER = custom_honeypot_error # Make honeypot errors less suspicious -HONEYPOT_FIELD_NAME_FORUM = "message2" # Only used on forum +HONEYPOT_FIELD_NAME_FORUM = env.str( + "HONEYPOT_FIELD_NAME_FORUM", default="message2" +) # Only used on forum # Email -EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" -EMAIL_HOST = "localhost" -EMAIL_PORT = 25 +EMAIL_BACKEND = env.str( + "EMAIL_BACKEND", default="django.core.mail.backends.dummy.EmailBackend" +) +EMAIL_HOST = env.str("EMAIL_HOST", default="localhost") +EMAIL_PORT = env.int("EMAIL_PORT", default=25) # Below this line, only Sith-specific variables are defined -SITH_URL = "my.url.git.an" -SITH_NAME = "Sith website" +SITH_URL = env.str("SITH_URL", default="127.0.0.1:8000") +SITH_NAME = env.str("SITH_NAME", default="AE UTBM") SITH_TWITTER = "@ae_utbm" # Enable experimental features @@ -321,7 +331,7 @@ SITH_ENABLE_GALAXY = False # AE configuration # TODO: keep only that first setting, with the ID, and do the same for the other clubs -SITH_MAIN_CLUB_ID = 1 +SITH_MAIN_CLUB_ID = env.int("SITH_MAIN_CLUB_ID", default=1) SITH_MAIN_CLUB = { "name": "AE", "unix_name": "ae", @@ -356,26 +366,28 @@ SITH_SCHOOL_START_YEAR = 1999 # id of the Root account SITH_ROOT_USER_ID = 0 -SITH_GROUP_ROOT_ID = 1 -SITH_GROUP_PUBLIC_ID = 2 -SITH_GROUP_SUBSCRIBERS_ID = 3 -SITH_GROUP_OLD_SUBSCRIBERS_ID = 4 -SITH_GROUP_ACCOUNTING_ADMIN_ID = 5 -SITH_GROUP_COM_ADMIN_ID = 6 -SITH_GROUP_COUNTER_ADMIN_ID = 7 -SITH_GROUP_SAS_ADMIN_ID = 8 -SITH_GROUP_FORUM_ADMIN_ID = 9 -SITH_GROUP_PEDAGOGY_ADMIN_ID = 10 +SITH_GROUP_ROOT_ID = env.int("SITH_GROUP_ROOT_ID", default=1) +SITH_GROUP_PUBLIC_ID = env.int("SITH_GROUP_PUBLIC_ID", default=2) +SITH_GROUP_SUBSCRIBERS_ID = env.int("SITH_GROUP_SUBSCRIBERS_ID", default=3) +SITH_GROUP_OLD_SUBSCRIBERS_ID = env.int("SITH_GROUP_OLD_SUBSCRIBERS_ID", default=4) +SITH_GROUP_ACCOUNTING_ADMIN_ID = env.int("SITH_GROUP_ACCOUNTING_ADMIN_ID", default=5) +SITH_GROUP_COM_ADMIN_ID = env.int("SITH_GROUP_COM_ADMIN_ID", default=6) +SITH_GROUP_COUNTER_ADMIN_ID = env.int("SITH_GROUP_COUNTER_ADMIN_ID", default=7) +SITH_GROUP_SAS_ADMIN_ID = env.int("SITH_GROUP_SAS_ADMIN_ID", default=8) +SITH_GROUP_FORUM_ADMIN_ID = env.int("SITH_GROUP_FORUM_ADMIN_ID", default=9) +SITH_GROUP_PEDAGOGY_ADMIN_ID = env.int("SITH_GROUP_PEDAGOGY_ADMIN_ID", default=10) -SITH_GROUP_BANNED_ALCOHOL_ID = 11 -SITH_GROUP_BANNED_COUNTER_ID = 12 -SITH_GROUP_BANNED_SUBSCRIPTION_ID = 13 +SITH_GROUP_BANNED_ALCOHOL_ID = env.int("SITH_GROUP_BANNED_ALCOHOL_ID", default=11) +SITH_GROUP_BANNED_COUNTER_ID = env.int("SITH_GROUP_BANNED_COUNTER_ID", default=12) +SITH_GROUP_BANNED_SUBSCRIPTION_ID = env.int( + "SITH_GROUP_BANNED_SUBSCRIPTION_ID", default=13 +) -SITH_CLUB_REFOUND_ID = 89 -SITH_COUNTER_REFOUND_ID = 38 -SITH_PRODUCT_REFOUND_ID = 5 +SITH_CLUB_REFOUND_ID = env.int("SITH_CLUB_REFOUND_ID", default=89) +SITH_COUNTER_REFOUND_ID = env.int("SITH_COUNTER_REFOUND_ID", default=38) +SITH_PRODUCT_REFOUND_ID = env.int("SITH_PRODUCT_REFOUND_ID", default=5) -SITH_COUNTER_ACCOUNT_DUMP_ID = 39 +SITH_COUNTER_ACCOUNT_DUMP_ID = env.int("SITH_COUNTER_ACCOUNT_DUMP_ID", default=39) # Pages SITH_CORE_PAGE_SYNTAX = "Aide_sur_la_syntaxe" @@ -385,7 +397,7 @@ SITH_CORE_PAGE_SYNTAX = "Aide_sur_la_syntaxe" SITH_FORUM_PAGE_LENGTH = 30 # SAS variables -SITH_SAS_ROOT_DIR_ID = 4 +SITH_SAS_ROOT_DIR_ID = env.int("SITH_SAS_ROOT_DIR_ID", default=4) SITH_SAS_IMAGES_PER_PAGE = 60 SITH_BOARD_SUFFIX = "-bureau" @@ -492,9 +504,9 @@ SITH_LOG_OPERATION_TYPE = [ SITH_PEDAGOGY_UTBM_API = "https://extranet1.utbm.fr/gpedago/api/guide" -SITH_ECOCUP_CONS = 1152 +SITH_ECOCUP_CONS = env.int("SITH_ECOCUP_CONS", default=1151) -SITH_ECOCUP_DECO = 1151 +SITH_ECOCUP_DECO = env.int("SITH_ECOCUP_DECO", default=1152) # The limit is the maximum difference between cons and deco possible for a customer SITH_ECOCUP_LIMIT = 3 @@ -509,21 +521,31 @@ SITH_ACCOUNT_DUMP_DELTA = timedelta(days=30) # Defines which product type is the refilling type, # and thus increases the account amount -SITH_COUNTER_PRODUCTTYPE_REFILLING = 3 +SITH_COUNTER_PRODUCTTYPE_REFILLING = env.int( + "SITH_COUNTER_PRODUCTTYPE_REFILLING", default=3 +) # Defines which product is the one year subscription # and which one is the six month subscription -SITH_PRODUCT_SUBSCRIPTION_ONE_SEMESTER = 1 -SITH_PRODUCT_SUBSCRIPTION_TWO_SEMESTERS = 2 -SITH_PRODUCTTYPE_SUBSCRIPTION = 2 +SITH_PRODUCT_SUBSCRIPTION_ONE_SEMESTER = env.int( + "SITH_PRODUCT_SUBSCRIPTION_ONE_SEMESTER", default=1 +) +SITH_PRODUCT_SUBSCRIPTION_TWO_SEMESTERS = env.int( + "SITH_PRODUCT_SUBSCRIPTION_TWO_SEMESTERS", default=2 +) +SITH_PRODUCTTYPE_SUBSCRIPTION = env.int("SITH_PRODUCTTYPE_SUBSCRIPTION", default=2) # Defines which club lets its member the ability to make subscriptions # Elements of this list are club's id -SITH_CAN_CREATE_SUBSCRIPTIONS = [1] +SITH_CAN_CREATE_SUBSCRIPTIONS = env.list( + "SITH_CAN_CREATE_SUBSCRIPTION_HISTORY", default=[1] +) # Defines which clubs lets its members the ability to see users subscription history # Elements of this list are club's id -SITH_CAN_READ_SUBSCRIPTION_HISTORY = [] +SITH_CAN_READ_SUBSCRIPTION_HISTORY = env.list( + "SITH_CAN_READ_SUBSCRIPTION_HISTORY", default=[1] +) # Number of weeks before the end of a subscription when the subscriber can resubscribe SITH_SUBSCRIPTION_END = 10 @@ -632,21 +654,29 @@ SITH_BARMAN_TIMEOUT = 30 SITH_LAST_OPERATIONS_LIMIT = 10 # ET variables -SITH_EBOUTIC_CB_ENABLED = True -SITH_EBOUTIC_ET_URL = ( - "https://preprod-tpeweb.e-transactions.fr/cgi/MYchoix_pagepaiement.cgi" +SITH_EBOUTIC_CB_ENABLED = env.bool("SITH_EBOUTIC_CB_ENABLED", default=True) +SITH_EBOUTIC_ET_URL = env.str( + "SITH_EBOUTIC_ET_URL", + default="https://preprod-tpeweb.e-transactions.fr/cgi/MYchoix_pagepaiement.cgi", ) -SITH_EBOUTIC_PBX_SITE = "1999888" -SITH_EBOUTIC_PBX_RANG = "32" -SITH_EBOUTIC_PBX_IDENTIFIANT = "2" +SITH_EBOUTIC_PBX_SITE = env.str("SITH_EBOUTIC_PBX_SITE", default="1999888") +SITH_EBOUTIC_PBX_RANG = env.str("SITH_EBOUTIC_PBX_RANG", default="32") +SITH_EBOUTIC_PBX_IDENTIFIANT = env.str("SITH_EBOUTIC_PBX_IDENTIFIANT", default="2") SITH_EBOUTIC_HMAC_KEY = binascii.unhexlify( - "0123456789ABCDEF0123456789ABCDEF" - "0123456789ABCDEF0123456789ABCDEF" - "0123456789ABCDEF0123456789ABCDEF" - "0123456789ABCDEF0123456789ABCDEF" + env.str( + "SITH_EBOUTIC_HMAC_KEY", + default=( + "0123456789ABCDEF0123456789ABCDEF" + "0123456789ABCDEF0123456789ABCDEF" + "0123456789ABCDEF0123456789ABCDEF" + "0123456789ABCDEF0123456789ABCDEF" + ), + ) ) SITH_EBOUTIC_PUB_KEY = "" -with open(os.path.join(os.path.dirname(__file__), "et_keys/pubkey.pem")) as f: +with open( + env.path("SITH_EBOUTIC_PUB_KEY_PATH", default=BASE_DIR / "sith/et_keys/pubkey.pem") +) as f: SITH_EBOUTIC_PUB_KEY = f.read() # Launderette variables @@ -688,24 +718,17 @@ SITH_QUICK_NOTIF = { # Mailing related settings SITH_MAILING_DOMAIN = "utbm.fr" -SITH_MAILING_FETCH_KEY = "IloveMails" +SITH_MAILING_FETCH_KEY = env.str("SITH_MAILING_FETCH_KEY", default="ILoveMails") SITH_GIFT_LIST = [("AE Tee-shirt", _("AE tee-shirt"))] -SENTRY_DSN = "" -SENTRY_ENV = "production" +SENTRY_DSN = env.str("SENRY_DSN", default=None) +SENTRY_ENV = env.str("SENTRY_ENV", default="production") TOXIC_DOMAINS_PROVIDERS = [ "https://www.stopforumspam.com/downloads/toxic_domains_whole.txt", ] -try: - from .settings_custom import * # noqa F403 (this star-import is actually useful) - - logging.getLogger("django").info("Custom settings imported") -except ImportError: - logging.getLogger("django").warning("Custom settings failed") - if DEBUG: INSTALLED_APPS += ("debug_toolbar",) MIDDLEWARE = ("debug_toolbar.middleware.DebugToolbarMiddleware", *MIDDLEWARE) diff --git a/sith/urls.py b/sith/urls.py index 95f22662..f6f7c8bb 100644 --- a/sith/urls.py +++ b/sith/urls.py @@ -81,7 +81,7 @@ def sentry_debug(request): The error will be displayed on Sentry inside the "development" environment - NOTE : you need to specify the SENTRY_DSN setting in settings_custom.py + NOTE : you need to specify the SENTRY_DSN setting in .env """ if settings.SENTRY_ENV != "development" or not settings.SENTRY_DSN: raise Http404 diff --git a/uv.lock b/uv.lock index 2ea5e370..441b0279 100644 --- a/uv.lock +++ b/uv.lock @@ -155,7 +155,7 @@ name = "click" version = "8.1.8" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "colorama", marker = "platform_system == 'Windows'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } wheels = [ @@ -276,6 +276,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973 }, ] +[[package]] +name = "dj-database-url" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "django" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/9f/fc9905758256af4f68a55da94ab78a13e7775074edfdcaddd757d4921686/dj_database_url-2.3.0.tar.gz", hash = "sha256:ae52e8e634186b57e5a45e445da5dc407a819c2ceed8a53d1fac004cc5288787", size = 10980 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/91/641a4e5c8903ed59f6cbcce571003bba9c5d2f731759c31db0ba83bb0bdb/dj_database_url-2.3.0-py3-none-any.whl", hash = "sha256:bb0d414ba0ac5cd62773ec7f86f8cc378a9dbb00a80884c2fc08cc570452521e", size = 7793 }, +] + +[[package]] +name = "dj-email-url" +version = "1.0.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/14/ef/8eb478accd9b0369d46a98d1b43027ee0c254096149265c78e6b2e2fa3b0/dj-email-url-1.0.6.tar.gz", hash = "sha256:55ffe3329e48f54f8a75aa36ece08f365e09d61f8a209773ef09a1d4760e699a", size = 15590 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/f9/fcb9745099d821f9a26092d3d6f4df8f10049885045c3a93ff726d2e40a6/dj_email_url-1.0.6-py2.py3-none-any.whl", hash = "sha256:cbd08327fbb08b104eac160fb4703f375532e4c0243eb230f5b960daee7a96db", size = 6296 }, +] + [[package]] name = "django" version = "4.2.17" @@ -290,6 +312,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/5e/85/457360cb3de496382e35db4c2af054066df5c40e26df31400d0109a0500c/Django-4.2.17-py3-none-any.whl", hash = "sha256:3a93350214ba25f178d4045c0786c61573e7dbfa3c509b3551374f1e11ba8de0", size = 7993390 }, ] +[[package]] +name = "django-cache-url" +version = "3.4.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/28/d420aaa89028d2ec0cf17c1510d06ff6a8ed0bf1abfb7f33c999e1c5befa/django-cache-url-3.4.5.tar.gz", hash = "sha256:eb9fb194717524348c95cad9905b70b647452741c1d9e481fac6d2125f0ad917", size = 7230 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/90/01755e4a42558b763f7021e9369aa6aa94c2ede7313deed56cb7483834ab/django_cache_url-3.4.5-py2.py3-none-any.whl", hash = "sha256:5f350759978483ab85dc0e3e17b3d53eed3394a28148f6bf0f53d11d0feb5b3c", size = 4760 }, +] + [[package]] name = "django-countries" version = "7.6.1" @@ -439,6 +470,26 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/93/69/e391bd51bc08ed9141ecd899a0ddb61ab6465309f1eb470905c0c8868081/docutils-0.19-py3-none-any.whl", hash = "sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc", size = 570472 }, ] +[[package]] +name = "environs" +version = "14.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "marshmallow" }, + { name = "python-dotenv" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3c/8f/952bd034eac79c8b68b6c770cb78c2bdcb3140d31ff224847f3520077d75/environs-14.1.0.tar.gz", hash = "sha256:a5f2afe9d5a21b468e74a3cceacf5d2371fd67dbb9a7e54fe62290c75a09cdfa", size = 30985 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/ad/57cfa3e8a006df88e723524127dbab2971a4877c97e7bad070257e15cb6c/environs-14.1.0-py3-none-any.whl", hash = "sha256:a7edda1668ddf1fbfcb7662bdc242dac25648eff2c7fdbaa5d959693afed7a3e", size = 15332 }, +] + +[package.optional-dependencies] +django = [ + { name = "dj-database-url" }, + { name = "dj-email-url" }, + { name = "django-cache-url" }, +] + [[package]] name = "executing" version = "2.1.0" @@ -708,6 +759,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, ] +[[package]] +name = "marshmallow" +version = "3.25.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bd/5c/cbfa41491d6c83b36471f2a2f75602349d20a8f88afd94f83c1e68bbc298/marshmallow-3.25.0.tar.gz", hash = "sha256:5ba94a4eb68894ad6761a505eb225daf7e5cb7b4c32af62d4a45e9d42665bc31", size = 176751 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/26/b347619b719d4c048e038929769f8f6b28c6d930149b40d950bbdde31d48/marshmallow-3.25.0-py3-none-any.whl", hash = "sha256:50894cd57c6b097a6c6ed2bf216af47d10146990a54db52d03e32edb0448c905", size = 49480 }, +] + [[package]] name = "matplotlib-inline" version = "0.1.7" @@ -744,7 +807,7 @@ version = "1.6.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "click" }, - { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "colorama", marker = "platform_system == 'Windows'" }, { name = "ghp-import" }, { name = "jinja2" }, { name = "markdown" }, @@ -1229,6 +1292,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, ] +[[package]] +name = "python-dotenv" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bc/57/e84d88dfe0aec03b7a2d4327012c1627ab5f03652216c63d49846d7a6c58/python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", size = 39115 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/3e/b68c118422ec867fa7ab88444e1274aa40681c606d59ac27de5a5588f082/python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a", size = 19863 }, +] + [[package]] name = "pyyaml" version = "6.0.2" @@ -1428,6 +1500,7 @@ dependencies = [ { name = "django-ordered-model" }, { name = "django-phonenumber-field" }, { name = "django-simple-captcha" }, + { name = "environs", extra = ["django"] }, { name = "ical" }, { name = "jinja2" }, { name = "libsass" }, @@ -1436,6 +1509,7 @@ dependencies = [ { name = "pillow" }, { name = "pydantic-extra-types" }, { name = "python-dateutil" }, + { name = "redis", extra = ["hiredis"] }, { name = "reportlab" }, { name = "sentry-sdk" }, { name = "sphinx" }, @@ -1462,7 +1536,6 @@ docs = [ ] prod = [ { name = "psycopg", extra = ["c"] }, - { name = "redis", extra = ["hiredis"] }, ] tests = [ { name = "freezegun" }, @@ -1486,6 +1559,7 @@ requires-dist = [ { name = "django-ordered-model", specifier = ">=3.7.4,<4.0.0" }, { name = "django-phonenumber-field", specifier = ">=8.0.0,<9.0.0" }, { name = "django-simple-captcha", specifier = ">=0.6.0,<1.0.0" }, + { name = "environs", extras = ["django"], specifier = ">=14.1.0,<15.0.0" }, { name = "ical", specifier = ">=8.3.0,<9.0.0" }, { name = "jinja2", specifier = ">=3.1.4,<4.0.0" }, { name = "libsass", specifier = ">=0.23.0,<1.0.0" }, @@ -1494,6 +1568,7 @@ requires-dist = [ { name = "pillow", specifier = ">=11.0.0,<12.0.0" }, { name = "pydantic-extra-types", specifier = ">=2.10.1,<3.0.0" }, { name = "python-dateutil", specifier = ">=2.9.0.post0,<3.0.0.0" }, + { name = "redis", extras = ["hiredis"], specifier = ">=5.2.0,<6.0.0" }, { name = "reportlab", specifier = ">=4.2.5,<5.0.0" }, { name = "sentry-sdk", specifier = ">=2.19.2,<3.0.0" }, { name = "sphinx", specifier = ">=5,<6" }, @@ -1518,10 +1593,7 @@ docs = [ { name = "mkdocstrings", specifier = ">=0.27.0,<1.0.0" }, { name = "mkdocstrings-python", specifier = ">=1.12.2,<2.0.0" }, ] -prod = [ - { name = "psycopg", extras = ["c"], specifier = ">=3.2.3,<4.0.0" }, - { name = "redis", extras = ["hiredis"], specifier = ">=5.2.0,<6.0.0" }, -] +prod = [{ name = "psycopg", extras = ["c"], specifier = ">=3.2.3,<4.0.0" }] tests = [ { name = "freezegun", specifier = ">=1.5.1,<2.0.0" }, { name = "model-bakery", specifier = ">=1.20.0,<2.0.0" },