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)
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 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.
- 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-<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
```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.<locale>.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.<locale>.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 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
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)

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

View file

@ -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", ""),
("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")

View file

@ -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

View file

@ -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(),
}

View file

@ -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