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.
This commit is contained in:
Egor Pavlovich Gorbunov 2025-10-21 12:23:02 +03:00
parent 4757634ce5
commit 3b7c405e84
6 changed files with 173 additions and 119 deletions

201
README.md
View file

@ -2,153 +2,138 @@
![LOGO](core/docs/images/evibes-big.png) ![LOGO](core/docs/images/evibes-big.png)
eVibes — your store without the extra baggage. eVibes — a lightweight, production-ready ecommerce backend. Storefront, product catalog, cart, and orders work out of the box. Minimal complexity, maximum flexibility — install, adjust to your needs, and start selling.
Everything works out of the box: storefront, product catalog, cart, and orders.
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 ## Table of Contents
- [Features](#features) - Features
- [Getting Started](#getting-started) - Quick Start
- [Prerequisites](#prerequisites) - Prerequisites
- [Installation](#installation) - Installation
- [Configuration](#configuration) - Configuration
- [Dockerfile](#Dockerfile) - Dockerfile
- [nginx](#nginx) - nginx
- [.env](#env) - .env
- [Usage](#usage) - Usage
- [Contact](#contact) - Contributing
- Contact
- License
## Features ## Features
- **Modular Architecture**: Extend and customize the backend to fit your needs. - Modular backend, easy to extend and customize
- **Dockerized Deployment**: Quick setup and deployment using Docker and Docker Compose. - Dockerized deployment with Docker Compose
- **Asynchronous Task Processing**: Integrated Celery workers and beat scheduler for background tasks. - Celery workers and beat for background tasks
- **GraphQL and REST APIs**: Supports both GraphQL and RESTful API endpoints. - REST and GraphQL APIs
- **Internationalization**: Multilingual support using modeltranslate. - Internationalization with modeltranslation
- **Advanced Caching**: Utilizes Redis for caching and task queuing. - Redis-based caching and queues
- **Security**: Implements JWT authentication and rate limiting. - JWT auth and rate limiting
## Getting Started ## Quick Start
### Prerequisites ### Prerequisites
- Docker and Docker Compose are installed on your machine. - Docker and Docker Compose
### Installation ### Installation
1. Clone the repository: 1. Clone the repository
```bash ```bash
git clone https://gitlab.com/wiseless.xyz/eVibes.git git clone https://gitlab.com/wiseless.xyz/eVibes.git
cd eVibes cd eVibes
``` ```
2. Choose the storefront. By default, `main` branch has no storefront included. 2. Choose a storefront (optional). The `main` branch ships without a storefront. If you want one, pick a branch:
Skip this step if you're OK with that and plan to only use API or develop your own storefront.
```bash ```bash
git checkout storefront-<options: nuxt, next, sk, qwik > git checkout storefront-<nuxt|next|sk|qwik>
``` ```
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 4. Install dependencies
```powershell - Windows
scripts\Windows\generate-environment-file.ps1 ```powershell
``` scripts\Windows\install.ps1
- Unix ```
```bash - Unix
scripts/Unix/generate-environment-file.sh ```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 6. Production checklist
```powershell - Include `nginx.conf` into your Nginx setup
scripts\Windows\install.ps1 - Issue TLS certs with Certbot (https://certbot.eff.org/)
```
- 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!
## Configuration ## Configuration
### Dockerfile ### Dockerfile
If you rely on locale mirrors, adjust Debian sources before running install scripts:
Remember to change the ```
`RUN sed -i 's|https://deb.debian.org/debian|https://ftp.<locale>.debian.org/debian|g' /etc/apt/sources.list.d/debian.sources` RUN sed -i 's|https://deb.debian.org/debian|https://ftp.<locale>.debian.org/debian|g' /etc/apt/sources.list.d/debian.sources
before running installment scripts ```
### nginx ### nginx
- Comment out SSL-related lines
Please comment-out SSL-related lines, then apply necessary configurations, run `certbot --cert-only --nginx`, - Apply your domain-specific settings
decomment previously commented lines, and enjoy eVibes over HTTPS! - Run `certbot --cert-only --nginx`
- Uncomment SSL lines and reload Nginx
### .env ### .env
After generation, review and update secrets and credentials (API keys, DB password, Redis password, etc.).
After .env file generation, you may want to edit some of its values, such as macroservices` API keys, database password,
redis password, etc.
## Usage ## Usage
- Add the necessary subdomains to DNS-settings of your domain, those are: - DNS records youll 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 - For local development, add hosts entries (development only):
2. www.your-domain.com ```hosts
3. api.your-domain.com 127.0.0.1 api.localhost
4. b2b.your-domain.com 127.0.0.1 b2b.localhost
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
``` ```
- 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 ## Contact
- **Author**: Egor "fureunoir" Gorbunov - Author: Egor "fureunoir" Gorbunov
- Email: contact@fureunoir.com - Email: contact@fureunoir.com
- Telegram: [@fureunoir](https://t.me/fureunoir) - 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) ![FAVICON](core/docs/images/evibes.png)

View file

@ -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( pages.append(
{ {
"name": static_post_page.title, "name": static_post_page.title,

View file

@ -222,28 +222,64 @@ LANGUAGES: tuple[tuple[str, str], ...] = (
LANGUAGE_CODE: str = "en-gb" LANGUAGE_CODE: str = "en-gb"
CURRENCIES: tuple[tuple[str, str], ...] = ( CURRENCIES_BY_LANGUAGES: tuple[tuple[str, str], ...] = (
("en-gb", "EUR"),
("ar-ar", "AED"), ("ar-ar", "AED"),
("cs-cz", "CZK"), ("cs-cz", "CZK"),
("da-dk", "EUR"), ("da-dk", "DKK"),
("de-de", "EUR"), ("de-de", "EUR"),
("en-gb", "GBP"),
("en-us", "USD"), ("en-us", "USD"),
("es-es", "EUR"), ("es-es", "EUR"),
("fa-ir", "IRR"),
("fr-fr", "EUR"), ("fr-fr", "EUR"),
("he-il", "ILS"),
("hi-in", "INR"), ("hi-in", "INR"),
("hr-hr", "EUR"),
("id-id", "IDR"),
("it-it", "EUR"), ("it-it", "EUR"),
("ja-jp", "JPY"), ("ja-jp", "JPY"),
("kk-kz", "KZT"), ("kk-kz", "KZT"),
("ko-kr", "KRW"),
("nl-nl", "EUR"), ("nl-nl", "EUR"),
("no-no", "NOK"),
("pl-pl", "PLN"), ("pl-pl", "PLN"),
("pt-br", "EUR"), ("pt-br", "BRL"),
("ro-ro", "RON"), ("ro-ro", "RON"),
("ru-ru", "RUB"), ("ru-ru", "RUB"),
("sv-se", "SEK"),
("th-th", "THB"),
("tr-tr", "TRY"),
("vi-vn", "VND"),
("zh-hans", "CNY"), ("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", ""),
("DKK", "kr"),
("EUR", ""),
("GBP", "£"),
("IDR", "Rp"),
("ILS", ""),
("INR", ""),
("IRR", ""),
("JPY", "¥"),
("KRW", ""),
("KZT", ""),
("NOK", "kr"),
("PLN", ""),
("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") MODELTRANSLATION_FALLBACK_LANGUAGES: tuple[str, ...] = (LANGUAGE_CODE, "en-us", "de-de")

View file

@ -5,7 +5,7 @@ from django.http import HttpRequest
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from core.admin import ActivationActionsMixin from core.admin import ActivationActionsMixin
from payments.forms import TransactionForm from payments.forms import TransactionForm, GatewayForm
from payments.models import Balance, Transaction from payments.models import Balance, Transaction
@ -41,3 +41,17 @@ class TransactionAdmin(ActivationActionsMixin, ModelAdmin): # type: ignore [mis
list_filter = ("currency", "payment_method") list_filter = ("currency", "payment_method")
ordering = ("balance",) ordering = ("balance",)
form = TransactionForm form = TransactionForm
class GatewayAdmin(ActivationActionsMixin, ModelAdmin):
list_display = (
"name",
"can_be_used",
"is_active",
)
search_fields = (
"name",
"default_currency",
)
ordering = ("name",)
form = GatewayForm

View file

@ -1,7 +1,7 @@
from django import forms from django import forms
from core.widgets import JSONTableWidget from core.widgets import JSONTableWidget
from payments.models import Transaction from payments.models import Gateway, Transaction
class TransactionForm(forms.ModelForm): # type: ignore [type-arg] class TransactionForm(forms.ModelForm): # type: ignore [type-arg]
@ -11,3 +11,12 @@ class TransactionForm(forms.ModelForm): # type: ignore [type-arg]
widgets = { widgets = {
"process": JSONTableWidget(), "process": JSONTableWidget(),
} }
class GatewayForm(forms.ModelForm): # type: ignore [type-arg]
class Meta:
model = Gateway
fields = "__all__"
widgets = {
"integration_variables": JSONTableWidget(),
}

View file

@ -85,14 +85,21 @@ class Balance(NiceModel):
class Gateway(NiceModel): class Gateway(NiceModel):
name = CharField(max_length=20, null=False, blank=False, verbose_name=_("name")) name = CharField(max_length=20, null=False, blank=False, verbose_name=_("name"))
default_currency = CharField( 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( currencies = CharField(
max_length=3, max_length=255,
null=False, null=False,
blank=False, blank=False,
verbose_name=_("currencies"), 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) integration_path = CharField(max_length=255, null=True, blank=True)
minimum_transaction_amount = FloatField( minimum_transaction_amount = FloatField(
@ -116,6 +123,7 @@ class Gateway(NiceModel):
help_text=_("monthly sum limit of transactions' amounts. 0 means no limit"), 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) 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): def __str__(self):
return self.name return self.name