From 3b7c405e84fd400ba4e9527ed0d4a1c3c374f377 Mon Sep 17 00:00:00 2001 From: Egor fureunoir Gorbunov Date: Tue, 21 Oct 2025 12:23:02 +0300 Subject: [PATCH] Features: 1) Add `GatewayForm` to manage Gateway model; 2) Enhance `GatewayAdmin` with list display, search, and ordering; 3) Introduce `integration_variables` field in the Gateway model; 4) Expand support for currencies with symbols via `CURRENCIES_WITH_SYMBOLS`; Fixes: 1) Add missing Gateway import in forms and admin modules; 2) Correct maximum lengths for currency fields in the Gateway model; Extra: 1) Refactor README for clarity and conciseness; 2) Adjust formatting in `core/sitemaps.py` for readability. --- README.md | 201 +++++++++++++++++++--------------------- core/sitemaps.py | 4 +- evibes/settings/base.py | 46 ++++++++- payments/admin.py | 16 +++- payments/forms.py | 11 ++- payments/models.py | 14 ++- 6 files changed, 173 insertions(+), 119 deletions(-) diff --git a/README.md b/README.md index 11ed606f..83e4e865 100644 --- a/README.md +++ b/README.md @@ -2,153 +2,138 @@ ![LOGO](core/docs/images/evibes-big.png) -eVibes — your store without the extra baggage. -Everything works out of the box: storefront, product catalog, cart, and orders. -Minimal complexity, maximum flexibility — install, adjust to your needs, and start selling. +eVibes — a lightweight, production-ready e‑commerce backend. Storefront, product catalog, cart, and orders work out of the box. Minimal complexity, maximum flexibility — install, adjust to your needs, and start selling. + +- Public issues: https://plane.wiseless.xyz/spaces/issues/dd33cb0ab9b04ef08a10f7eefae6d90c/?board=kanban ## Table of Contents -- [Features](#features) -- [Getting Started](#getting-started) - - [Prerequisites](#prerequisites) - - [Installation](#installation) -- [Configuration](#configuration) - - [Dockerfile](#Dockerfile) - - [nginx](#nginx) - - [.env](#env) -- [Usage](#usage) -- [Contact](#contact) +- Features +- Quick Start + - Prerequisites + - Installation +- Configuration + - Dockerfile + - nginx + - .env +- Usage +- Contributing +- Contact +- License ## Features -- **Modular Architecture**: Extend and customize the backend to fit your needs. -- **Dockerized Deployment**: Quick setup and deployment using Docker and Docker Compose. -- **Asynchronous Task Processing**: Integrated Celery workers and beat scheduler for background tasks. -- **GraphQL and REST APIs**: Supports both GraphQL and RESTful API endpoints. -- **Internationalization**: Multilingual support using modeltranslate. -- **Advanced Caching**: Utilizes Redis for caching and task queuing. -- **Security**: Implements JWT authentication and rate limiting. +- Modular backend, easy to extend and customize +- Dockerized deployment with Docker Compose +- Celery workers and beat for background tasks +- REST and GraphQL APIs +- Internationalization with modeltranslation +- Redis-based caching and queues +- JWT auth and rate limiting -## Getting Started +## Quick Start ### Prerequisites -- Docker and Docker Compose are installed on your machine. +- Docker and Docker Compose ### Installation -1. Clone the repository: - +1. Clone the repository ```bash git clone https://gitlab.com/wiseless.xyz/eVibes.git cd eVibes ``` -2. Choose the storefront. By default, `main` branch has no storefront included. -Skip this step if you're OK with that and plan to only use API or develop your own storefront. - +2. Choose a storefront (optional). The `main` branch ships without a storefront. If you want one, pick a branch: ```bash - git checkout storefront- + git checkout storefront- ``` -3. Generate your .env file. Check and confirm the contents afterward. +3. Generate your .env file and review its values + - Windows + ```powershell + scripts\Windows\generate-environment-file.ps1 + ``` + - Unix + ```bash + scripts/Unix/generate-environment-file.sh + ``` - - Windows - ```powershell - scripts\Windows\generate-environment-file.ps1 - ``` - - Unix - ```bash - scripts/Unix/generate-environment-file.sh - ``` +4. Install dependencies + - Windows + ```powershell + scripts\Windows\install.ps1 + ``` + - Unix + ```bash + scripts/Unix/install.sh + ``` -4. Install all the dependencies. +5. Run the stack + - Windows + ```powershell + scripts\Windows\run.ps1 + ``` + - Unix + ```bash + scripts/Unix/run.sh + ``` - - Windows - ```powershell - scripts\Windows\install.ps1 - ``` - - Unix - ```bash - scripts/Unix/install.sh - ``` - -5. Spin it up. - - - Windows - ```powershell - scripts\Windows\run.ps1 - ``` - - Unix - ```bash - scripts/Unix/run.sh - ``` - -6. Bring to production. - - Include `nginx` file to your nginx configuration, you really want to install and - run [Certbot](https://certbot.eff.org/) afterward! +6. Production checklist + - Include `nginx.conf` into your Nginx setup + - Issue TLS certs with Certbot (https://certbot.eff.org/) ## Configuration ### Dockerfile - -Remember to change the -`RUN sed -i 's|https://deb.debian.org/debian|https://ftp..debian.org/debian|g' /etc/apt/sources.list.d/debian.sources` -before running installment scripts +If you rely on locale mirrors, adjust Debian sources before running install scripts: +``` +RUN sed -i 's|https://deb.debian.org/debian|https://ftp..debian.org/debian|g' /etc/apt/sources.list.d/debian.sources +``` ### nginx - -Please comment-out SSL-related lines, then apply necessary configurations, run `certbot --cert-only --nginx`, -decomment previously commented lines, and enjoy eVibes over HTTPS! +- Comment out SSL-related lines +- Apply your domain-specific settings +- Run `certbot --cert-only --nginx` +- Uncomment SSL lines and reload Nginx ### .env - -After .env file generation, you may want to edit some of its values, such as macroservices` API keys, database password, -redis password, etc. +After generation, review and update secrets and credentials (API keys, DB password, Redis password, etc.). ## Usage -- Add the necessary subdomains to DNS-settings of your domain, those are: +- DNS records you’ll typically want: + 1. @.your-domain.com + 2. www.your-domain.com + 3. api.your-domain.com + 4. b2b.your-domain.com + 5. prometheus.your-domain.com -1. @.your-domain.com -2. www.your-domain.com -3. api.your-domain.com -4. b2b.your-domain.com -5. prometheus.your-domain.com - -- Add these lines to your hosts-file to use django-hosts functionality on localhost(*DEVELOPMENT ONLY*): - -```hosts -127.0.0.1 api.localhost -127.0.0.1 b2b.localhost -``` - -Once the services are up and running, you can access the application at -`http://api.your-domain.com`(http://api.localhost:8000). - -- **Django Admin**: `http://api.your-domain.com/` (will redirect to admin) -- **API Docs**: - - REST API: `http://api.localhost:8000/docs/swagger` or `http://api.localhost:8000/docs/redoc` - - GraphQL API: `http://api.localhost:8000/graphql/` - -## Uninstall eVibes - -You are not planning to do that, aren't you? - -- Windows - ```powershell - scripts\Windows\uninstall.ps1 - ``` -- Unix - ```bash - scripts/Unix/uninstall.sh +- For local development, add hosts entries (development only): + ```hosts + 127.0.0.1 api.localhost + 127.0.0.1 b2b.localhost ``` +- Once running, access: + - API root / Admin redirect: http://api.localhost:8000/ + - REST docs: http://api.localhost:8000/docs/swagger or http://api.localhost:8000/docs/redoc + - GraphQL: http://api.localhost:8000/graphql/ + +## Contributing + +- Track and report issues here: https://plane.wiseless.xyz/spaces/issues/dd33cb0ab9b04ef08a10f7eefae6d90c/?board=list +- Pull requests are welcome. Please keep changes minimal and focused. + ## Contact -- **Author**: Egor "fureunoir" Gorbunov - - Email: contact@fureunoir.com - - Telegram: [@fureunoir](https://t.me/fureunoir) +- Author: Egor "fureunoir" Gorbunov + - Email: contact@fureunoir.com + - Telegram: https://t.me/fureunoir + +## License + +This project is licensed under the terms of the LICENSE file included in this repository. ![FAVICON](core/docs/images/evibes.png) \ No newline at end of file diff --git a/core/sitemaps.py b/core/sitemaps.py index 18496eb7..24db6dfc 100644 --- a/core/sitemaps.py +++ b/core/sitemaps.py @@ -38,7 +38,9 @@ class StaticPagesSitemap(SitemapLanguageMixin, Sitemap): # type: ignore [type-a }, ] - for static_post_page in Post.objects.filter(is_static_page=True, is_active=True).only("title", "slug", "modified"): + for static_post_page in Post.objects.filter(is_static_page=True, is_active=True).only( + "title", "slug", "modified" + ): pages.append( { "name": static_post_page.title, diff --git a/evibes/settings/base.py b/evibes/settings/base.py index 25c66bdf..9ec2107d 100644 --- a/evibes/settings/base.py +++ b/evibes/settings/base.py @@ -222,28 +222,64 @@ LANGUAGES: tuple[tuple[str, str], ...] = ( LANGUAGE_CODE: str = "en-gb" -CURRENCIES: tuple[tuple[str, str], ...] = ( - ("en-gb", "EUR"), +CURRENCIES_BY_LANGUAGES: tuple[tuple[str, str], ...] = ( ("ar-ar", "AED"), ("cs-cz", "CZK"), - ("da-dk", "EUR"), + ("da-dk", "DKK"), ("de-de", "EUR"), + ("en-gb", "GBP"), ("en-us", "USD"), ("es-es", "EUR"), + ("fa-ir", "IRR"), ("fr-fr", "EUR"), + ("he-il", "ILS"), ("hi-in", "INR"), + ("hr-hr", "EUR"), + ("id-id", "IDR"), ("it-it", "EUR"), ("ja-jp", "JPY"), ("kk-kz", "KZT"), + ("ko-kr", "KRW"), ("nl-nl", "EUR"), + ("no-no", "NOK"), ("pl-pl", "PLN"), - ("pt-br", "EUR"), + ("pt-br", "BRL"), ("ro-ro", "RON"), ("ru-ru", "RUB"), + ("sv-se", "SEK"), + ("th-th", "THB"), + ("tr-tr", "TRY"), + ("vi-vn", "VND"), ("zh-hans", "CNY"), ) -CURRENCY_CODE: str = dict(CURRENCIES).get(LANGUAGE_CODE) # type: ignore [assignment] +CURRENCIES_WITH_SYMBOLS: tuple[tuple[str, str], ...] = ( + ("AED", "د.إ"), + ("BRL", "R$"), + ("CNY", "¥"), + ("CZK", "Kč"), + ("DKK", "kr"), + ("EUR", "€"), + ("GBP", "£"), + ("IDR", "Rp"), + ("ILS", "₪"), + ("INR", "₹"), + ("IRR", "﷼"), + ("JPY", "¥"), + ("KRW", "₩"), + ("KZT", "₸"), + ("NOK", "kr"), + ("PLN", "zł"), + ("RON", "lei"), + ("RUB", "₽"), + ("SEK", "kr"), + ("THB", "฿"), + ("TRY", "₺"), + ("USD", "$"), + ("VND", "₫"), +) + +CURRENCY_CODE: str = dict(CURRENCIES_BY_LANGUAGES).get(LANGUAGE_CODE) # type: ignore[assignment] MODELTRANSLATION_FALLBACK_LANGUAGES: tuple[str, ...] = (LANGUAGE_CODE, "en-us", "de-de") diff --git a/payments/admin.py b/payments/admin.py index bc22b6dc..1f9e4c9c 100644 --- a/payments/admin.py +++ b/payments/admin.py @@ -5,7 +5,7 @@ from django.http import HttpRequest from django.utils.translation import gettext_lazy as _ from core.admin import ActivationActionsMixin -from payments.forms import TransactionForm +from payments.forms import TransactionForm, GatewayForm from payments.models import Balance, Transaction @@ -41,3 +41,17 @@ class TransactionAdmin(ActivationActionsMixin, ModelAdmin): # type: ignore [mis list_filter = ("currency", "payment_method") ordering = ("balance",) form = TransactionForm + + +class GatewayAdmin(ActivationActionsMixin, ModelAdmin): + list_display = ( + "name", + "can_be_used", + "is_active", + ) + search_fields = ( + "name", + "default_currency", + ) + ordering = ("name",) + form = GatewayForm diff --git a/payments/forms.py b/payments/forms.py index 00952e67..b752d00b 100644 --- a/payments/forms.py +++ b/payments/forms.py @@ -1,7 +1,7 @@ from django import forms from core.widgets import JSONTableWidget -from payments.models import Transaction +from payments.models import Gateway, Transaction class TransactionForm(forms.ModelForm): # type: ignore [type-arg] @@ -11,3 +11,12 @@ class TransactionForm(forms.ModelForm): # type: ignore [type-arg] widgets = { "process": JSONTableWidget(), } + + +class GatewayForm(forms.ModelForm): # type: ignore [type-arg] + class Meta: + model = Gateway + fields = "__all__" + widgets = { + "integration_variables": JSONTableWidget(), + } diff --git a/payments/models.py b/payments/models.py index 81f9a10d..a779ea0a 100644 --- a/payments/models.py +++ b/payments/models.py @@ -85,14 +85,21 @@ class Balance(NiceModel): class Gateway(NiceModel): name = CharField(max_length=20, null=False, blank=False, verbose_name=_("name")) default_currency = CharField( - max_length=3, null=False, blank=False, verbose_name=_("default currency"), choices=settings.CURRENCIES + max_length=4, + null=False, + blank=False, + verbose_name=_("default currency"), + choices=settings.CURRENCIES_WITH_SYMBOLS, ) currencies = CharField( - max_length=3, + max_length=255, null=False, blank=False, verbose_name=_("currencies"), - help_text=_(f"comma separated list of currencies supported by this gateway, choose from {settings.CURRENCIES}"), + help_text=_( + f"comma separated list of currencies supported by this gateway, " + f"choose from {', '.join([code for code, _ in settings.CURRENCIES_WITH_SYMBOLS])}" + ), ) integration_path = CharField(max_length=255, null=True, blank=True) minimum_transaction_amount = FloatField( @@ -116,6 +123,7 @@ class Gateway(NiceModel): help_text=_("monthly sum limit of transactions' amounts. 0 means no limit"), ) priority = PositiveIntegerField(null=False, blank=False, default=10, verbose_name=_("priority"), unique=True) + integration_variables = JSONField(null=False, blank=False, default=dict, verbose_name=_("integration variables")) def __str__(self): return self.name