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:
parent
4757634ce5
commit
3b7c405e84
6 changed files with 173 additions and 119 deletions
201
README.md
201
README.md
|
|
@ -2,153 +2,138 @@
|
|||
|
||||

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

|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue