This commit is contained in:
Sayyid Hamid Mahdavi 2025-05-10 11:52:08 +03:30
commit 85c76a2bc6
13 changed files with 591 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
.idea
.env
media
/clients/

10
Dockerfile Normal file
View file

@ -0,0 +1,10 @@
FROM debian:12
ENV PYTHONUNBUFFERED 1
WORKDIR /app
#RUN date
RUN apt update
RUN apt install gnupg2 netcat-traditional libssl-dev libcrypto++-dev lsb-release python3-pip -y
RUN apt install binutils libproj-dev gdal-bin -y
COPY requirements.txt /app/requirements.txt
RUN pip3 install --break-system-packages -r requirements.txt
#ENTRYPOINT ["./run.sh"]

22
manage.py Executable file
View file

@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'main.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

19
requirements.in Normal file
View file

@ -0,0 +1,19 @@
Django
python-decouple
psycopg[binary,pool]
django-jalali-date
django-crispy-forms
crispy_bootstrap5
djangorestframework
django-filter
django-cors-headers
gunicorn
gevent
drf-spectacular
pillow
requests
django-oauth-toolkit
redis
celery
httpx
django-redis

184
requirements.txt Normal file
View file

@ -0,0 +1,184 @@
#
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile
#
amqp==5.3.1
# via kombu
anyio==4.8.0
# via httpx
asgiref==3.8.1
# via
# django
# django-cors-headers
async-timeout==5.0.1
# via redis
attrs==24.3.0
# via
# jsonschema
# referencing
billiard==4.2.1
# via celery
celery==5.4.0
# via -r requirements.in
certifi==2024.12.14
# via
# httpcore
# httpx
# requests
cffi==1.17.1
# via cryptography
charset-normalizer==3.4.0
# via requests
click==8.1.7
# via
# celery
# click-didyoumean
# click-plugins
# click-repl
click-didyoumean==0.3.1
# via celery
click-plugins==1.1.1
# via celery
click-repl==0.3.0
# via celery
crispy-bootstrap5==2024.10
# via -r requirements.in
cryptography==44.0.0
# via jwcrypto
django==5.1.4
# via
# -r requirements.in
# crispy-bootstrap5
# django-cors-headers
# django-crispy-forms
# django-filter
# django-jalali-date
# django-oauth-toolkit
# django-redis
# djangorestframework
# drf-spectacular
django-cors-headers==4.6.0
# via -r requirements.in
django-crispy-forms==2.3
# via
# -r requirements.in
# crispy-bootstrap5
django-filter==24.3
# via -r requirements.in
django-jalali-date==1.1.3
# via -r requirements.in
django-oauth-toolkit==3.0.1
# via -r requirements.in
django-redis==5.4.0
# via -r requirements.in
djangorestframework==3.15.2
# via
# -r requirements.in
# drf-spectacular
drf-spectacular==0.28.0
# via -r requirements.in
gevent==24.11.1
# via -r requirements.in
greenlet==3.1.1
# via gevent
gunicorn==23.0.0
# via -r requirements.in
h11==0.14.0
# via httpcore
httpcore==1.0.7
# via httpx
httpx==0.28.1
# via -r requirements.in
idna==3.10
# via
# anyio
# httpx
# requests
inflection==0.5.1
# via drf-spectacular
jalali-core==1.0.0
# via jdatetime
jdatetime==5.0.0
# via django-jalali-date
jsonschema==4.23.0
# via drf-spectacular
jsonschema-specifications==2024.10.1
# via jsonschema
jwcrypto==1.5.6
# via django-oauth-toolkit
kombu==5.4.2
# via celery
oauthlib==3.2.2
# via django-oauth-toolkit
packaging==24.2
# via gunicorn
pillow==11.0.0
# via -r requirements.in
prompt-toolkit==3.0.48
# via click-repl
psycopg[binary,pool]==3.2.3
# via -r requirements.in
psycopg-binary==3.2.3
# via psycopg
psycopg-pool==3.2.4
# via psycopg
pycparser==2.22
# via cffi
python-dateutil==2.9.0.post0
# via celery
python-decouple==3.8
# via -r requirements.in
pyyaml==6.0.2
# via drf-spectacular
redis==5.2.1
# via
# -r requirements.in
# django-redis
referencing==0.35.1
# via
# jsonschema
# jsonschema-specifications
requests==2.32.3
# via
# -r requirements.in
# django-oauth-toolkit
rpds-py==0.22.3
# via
# jsonschema
# referencing
six==1.17.0
# via python-dateutil
sniffio==1.3.1
# via anyio
sqlparse==0.5.3
# via django
typing-extensions==4.12.2
# via
# anyio
# jwcrypto
# psycopg
# psycopg-pool
tzdata==2024.2
# via
# celery
# kombu
uritemplate==4.1.1
# via drf-spectacular
urllib3==2.2.3
# via requests
vine==5.1.0
# via
# amqp
# celery
# kombu
wcwidth==0.2.13
# via prompt-toolkit
zope-event==5.0
# via gevent
zope-interface==7.2
# via gevent
# The following packages are considered to be unsafe in a requirements file:
# setuptools

8
run.sh Executable file
View file

@ -0,0 +1,8 @@
#!/usr/bin/env bash
while ! nc -z $DB_HOST 5432 ; do
echo "APP Waiting for the DB Server"
sleep 3
done
#python3 manage.py collectstatic --noinput
python3 manage.py migrate
gunicorn main.wsgi:application --bind 0.0.0.0:8000 -w 4

125
templates/base.html Normal file
View file

@ -0,0 +1,125 @@
{% load static %}
{#{% load view_breadcrumbs %}#}
<!doctype html>
<html lang="fa-ir" dir="rtl">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<title>{% block title %}welcome{% endblock %}</title>
<link rel="manifest" href="{% static 'manifest.json' %}">
<link href="{% static 'bootstrap-5.3.0-dist/css/bootstrap.rtl.min.css' %}" rel="stylesheet">
<link href="{% static 'bootstrap-icons-1.11.3/font/bootstrap-icons.css' %}" rel="stylesheet">
<link href="{% static 'fonts/vazirmatn-v33.003/misc/UI-Farsi-Digits/Vazirmatn-UI-FD-font-face.css' %}"
rel="stylesheet">
<link href="{% static 'fonts/Pelak/fonts.css' %}" rel="stylesheet">
<link href="{% static 'main.css' %}" rel="stylesheet">
<script src="{% static 'jquery-3.6.0.min.js' %}"></script>
{# <link rel="stylesheet"#}
{# href="https://cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css"/>#}
<!-- Or for RTL support -->
<link rel="stylesheet"
href="{% static 'select2-bootstrap-5-theme-1.3.0/select2-bootstrap-5-theme.rtl.min.css' %}"/>
<link rel="stylesheet" href="{% static 'jalalidatepicker/jalalidatepicker.css' %}">
<script src="{% static 'jalalidatepicker/jalalidatepicker.js' %}"></script>
<style>
@media (min-width: 576px) {
.h-sm-100 {
height: 100%;
}
}
</style>
</head>
<body>
<div class="container-fluid">
<div class="row vh-100 overflow-auto">
{# {% include "include/sidebar.html" %}#}
<div class="col d-flex flex-column h-sm-100">
<div class="m-0 p-0 w-100 " style="background-color: green; height: 3px;"></div>
<main class="row overflow-auto pb-3">
<div class="col pt-2">
{% include "include/top_bar.html" %}
{% include "include/messages.html" %}
{# {% block breadcrumbs %}#}
{# {% render_breadcrumbs %}#}
{# {% endblock %}#}
<div class="row">
<div class="col">
<div class="grid">
<div class="d-flex justify-content-start">
<h4 class="m-3">
{% block page_title %}
{% endblock %}
</h4>
</div>
</div>
</div>
<div class="col">
<div class="grid">
<div class="d-flex justify-content-end mt-2">
{% block page_action %}
{% endblock %}
</div>
</div>
</div>
</div>
<hr>
<div class="row">
<div class="col">
<div class="card bg-light">
<div class="card-body">
<div class="col-md-12 col-lg-12">
{% block content %}
{% endblock %}
</div>
</div>
</div>
</div>
</div>
{# {% include 'include/footer.html' %}#}
</div>
</main>
<footer class="row bg-light py-3 mt-auto small">
<div class="d-flex">
<div class="flex-grow-1">
کلیه حقوق متعلق به
<a class="text-decoration-none" href="/">بیلبورد</a>
است.
</div>
{# <div class="">#}
{# توسعه#}
{# <a href="https://gooyal.com" class="text-decoration-none" target="_blank"#}
{# rel="noopener noreferrer">#}
{# گویال#}
{# <img src="{% static 'image/gooyal_logo_gray.png' %}" width="28" height="21">#}
{# </a>#}
{# </div>#}
</div>
</footer>
</div>
</div>
</div>
</body>
<script>
jalaliDatepicker.startWatch();
</script>
<script src="{% static 'bootstrap-5.3.0-dist/js/bootstrap.bundle.min.js' %}"></script>
<script src="{% static 'jquery.formset.js' %}"></script>
</html>

12
templates/core/home.html Normal file
View file

@ -0,0 +1,12 @@
{% extends "base.html" %}
{% load static %}
{% load crispy_forms_tags %}
{% load jalali_tags %}
{% block title %}IPG: Home{% endblock %}
{% block page_title %}IPG: Home{% endblock %}
{% block content %}
IPG
{% endblock %}

View file

@ -0,0 +1,33 @@
{% load crispy_forms_tags %}
<button type="button" class="btn btn-primary me-2" data-bs-toggle="modal" data-bs-target="#filterModal">
<span class="bi bi-funnel-fill"></span>
</button>
<!-- Modal -->
<div class="modal fade" id="filterModal" tabindex="-1" aria-labelledby="exampleModalLabel"
aria-hidden="true">
<div class="modal-dialog">
<form method="get">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">محدود کردن نتایج</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"
aria-label="بستن"></button>
</div>
<div class="modal-body">
{{ filter.form | crispy }}
</div>
<div class="modal-footer">
{# <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>#}
<input type="submit" class="btn btn-primary" value="اعمال">
{# <button type="button" class="btn btn-primary"></button>#}
</div>
</div>
</form>
</div>
</div>

View file

@ -0,0 +1,9 @@
{% if messages %}
{% for message in messages %}
{# <li{% if message.tags %} class="{{ message.tags }}"{% endif %}></li>#}
<div class="alert alert-success alert-dismissible fade show" role="alert">
<span> {{ message }} </span>
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
{% endif %}

View file

@ -0,0 +1,21 @@
{% load tags %}
<nav class="d-flex justify-content-center" aria-label="Page navigation example">
<ul class="pagination">
{% if is_paginated %}
{% get_proper_elided_page_range paginator page_obj.number as page_range %}
{% for i in page_range %}
{% if page_obj.number == i %}
<li class="page-item"><span class="page-link"><strong>( {{ i }} )</strong></span></li>
{% elif i == '…' %}
<li class="page-item"><span class="page-link">{{ i }}</span></li>
{% else %}
<li class="page-item"><a class="page-link" href="?{% get_filter_link request 'page' i %}">{{ i }}</a></li>
{% endif %}
{% endfor %}
{% endif %}
</ul>
<p class="p-2 mb-0"><span class="fw-bolder text-decoration-underline">{{ paginator.count }}</span> داده یافت شد.
</p>
</nav>

View file

@ -0,0 +1,21 @@
{% load static %}
<div class="card bg-light title">
<div class="card-body">
<div class="row g-3">
<div class="col d-flex justify-content-start">
<div class="bi p-1"><img src="{% static 'image/logo.png' %}" width="21" height="auto"></div>
{# <img class="bi bi-speedometer p-1"></img>#}
<div class="p-1"><a class="text-decoration-none" href="/">dashboard</a></div>
</div>
<div class="col d-flex justify-content-end">
<div class="bi p-1"><img src="{% static 'image/logo.png' %}" width="21" height="auto"></div>
{# <img class="bi bi-speedometer p-1"></img>#}
<div class="p-1"><a class="text-decoration-none" href="#">dashboard</a></div>
</div>
</div>
</div>
</div>
<hr>

123
utils/wallet_client.py Normal file
View file

@ -0,0 +1,123 @@
import time
import requests
from django.conf import settings
from django.core.cache import cache
from django.db import models
from httpx import Request
from gooyal_wallet_client import AuthenticatedClient
from gooyal_wallet_client.models import MyWallet, UserTransaction, ApplicationDeposit, ApplicationWithdraw
from gooyal_wallet_client.api.wallet import (wallet_api_application_deposit_create,
wallet_api_application_deposit_verify_retrieve,
wallet_api_application_withdraw_create,
wallet_api_application_withdraw_verify_retrieve)
from gooyal_wallet_client.types import Response
class ContentTypeChoices(models.IntegerChoices):
IMAGE = 1, 'image'
VOICE = 2, 'voice'
VIDEO = 3, 'video'
def login_as_client_credentials():
access_token = cache.get('ads_access_token')
# expires_in = self.auth_data.get('expires_in')
# created_at = self.auth_data.get('created_at')
#
# if access_token and expires_in > time.time() - created_at:
# return self.auth_data
if access_token:
print('already in cache')
return access_token
print('not in cache')
data = {
"grant_type": 'client_credentials',
"scope": settings.SCOPES,
}
auth = (settings.CLIENT_ID, settings.CLIENT_SECRET)
response = requests.post(f'{settings.BASE_OAUTH2_PROVIDER_PUBLIC_URL}/token/',
data=data,
auth=auth)
print(response.content)
auth_data = response.json()
if 'access_token' in auth_data:
auth_data['created_at'] = time.time()
cache.set('ads_access_token', auth_data, timeout=auth_data.get('expires_in') - 5)
return auth_data
def log_request(request: Request):
print(request.headers['authorization'])
print(
f"Request event hook: {request.method} {request.url} {request.method} {request.headers} - Waiting for response")
def log_response(response):
request = response.request
print(f"Response event hook: {request.method} {request.url} - Status {response.status_code}")
def get_client():
access_token = login_as_client_credentials()['access_token']
client = AuthenticatedClient(base_url="https://wallet.gooyal.com", token=access_token,
httpx_args={"event_hooks": {"request": [log_request], "response": [log_response]}},
)
return client
def deposit_to_user_wallet_submit(data):
class Tmp():
def to_dict(self):
return data
deposit_data = Tmp()
client = get_client()
response: ApplicationDeposit = wallet_api_application_deposit_create.sync(client=client, body=deposit_data)
print(response)
return response
def deposit_to_user_wallet_verify(uuid):
client = get_client()
response: ApplicationDeposit = wallet_api_application_deposit_verify_retrieve.sync(uuid=uuid, client=client)
print(response)
return response
def withdraw_from_user_wallet_submit(data):
class Tmp():
def to_dict(self):
return data
withdraw_data = Tmp()
client = get_client()
response: ApplicationWithdraw = wallet_api_application_withdraw_create.sync(client=client, body=withdraw_data)
print(response)
return response
def withdraw_from_user_wallet_verify(uuid):
client = get_client()
response: ApplicationWithdraw = wallet_api_application_withdraw_verify_retrieve.sync(uuid=uuid, client=client)
print(response)
return response
sample_data = {
"uuid": "3fa85f64-5717-4562-b3fc-2c963f66afb1",
"payee_id": "5d66d02e-6087-4b0a-bb0c-291d5c11755a",
"payee_type": 1,
"amount": 2000,
"details": "string",
}