Spaces:
Running
Running
# management/models.py | |
# -*- coding: utf-8 -*- | |
from django.db import models | |
from django.contrib.auth.models import AbstractUser | |
from django.utils import timezone | |
import uuid | |
import datetime | |
# --- Users & Authentication --- | |
class CustomUser(AbstractUser): | |
ROLE_CHOICES = [ | |
('SuperAdmin', 'Super Admin'), | |
('PanelOwner', 'Panel Owner'), | |
('AdminLevel2', 'Admin Level 2'), | |
('AdminLevel3', 'Admin Level 3'), | |
('EndUser', 'End User'), | |
] | |
role = models.CharField(max_length=20, choices=ROLE_CHOICES, default='EndUser') | |
marzban_admin_id = models.UUIDField(null=True, blank=True) | |
def is_super_admin(self): | |
return self.role == 'SuperAdmin' | |
def is_admin_level_2(self): | |
return self.role in ('SuperAdmin', 'PanelOwner', 'AdminLevel2') | |
def is_admin_level_3(self): | |
return self.role in ('SuperAdmin', 'PanelOwner', 'AdminLevel2', 'AdminLevel3') | |
def __str__(self): | |
return self.username | |
# --- Main Website Models --- | |
class AdminLevel2(models.Model): | |
user = models.OneToOneField( | |
CustomUser, on_delete=models.CASCADE, related_name='admin_level_2' | |
) | |
telegram_chat_id = models.CharField( | |
max_length=255, blank=True, null=True, | |
verbose_name="تلگرام چت آیدی" | |
) | |
license_expiry_date = models.DateField( | |
null=True, blank=True, verbose_name="تاریخ انقضای لایسنس" | |
) | |
def __str__(self): | |
return f"Admin Level 2: {self.user.username}" | |
class AdminLevel3(models.Model): | |
user = models.OneToOneField( | |
CustomUser, on_delete=models.CASCADE, related_name='admin_level_3' | |
) | |
parent_admin = models.ForeignKey( | |
AdminLevel2, | |
on_delete=models.CASCADE, | |
related_name='child_admins', | |
verbose_name="ادمین والد (سطح ۲)" | |
) | |
telegram_chat_id = models.CharField( | |
max_length=255, blank=True, null=True, | |
verbose_name="تلگرام چت آیدی" | |
) | |
def __str__(self): | |
return f"Admin Level 3: {self.user.username}" | |
class Panel(models.Model): | |
owner = models.OneToOneField(CustomUser, on_delete=models.CASCADE, related_name='owned_panel') | |
name = models.CharField(max_length=255, verbose_name="نام پنل") | |
marzban_host = models.CharField(max_length=255, verbose_name="آدرس هاست Marzban") | |
marzban_username = models.CharField(max_length=255, verbose_name="نام کاربری Marzban") | |
marzban_password = models.CharField(max_length=255, verbose_name="رمز عبور Marzban") | |
telegram_bot_token = models.CharField(max_length=255, blank=True, null=True) | |
# ADDED: Link to the license | |
license = models.OneToOneField('License', on_delete=models.SET_NULL, null=True, blank=True, related_name='linked_panel') | |
def __str__(self): | |
return self.name | |
class License(models.Model): | |
key = models.UUIDField(default=uuid.uuid4, editable=False, unique=True, verbose_name="کلید لایسنس") | |
is_active = models.BooleanField(default=True, verbose_name="وضعیت فعال") | |
owner = models.ForeignKey(CustomUser, on_delete=models.CASCADE, verbose_name="صاحب لایسنس") | |
created_at = models.DateTimeField(auto_now_add=True, verbose_name="تاریخ ایجاد") | |
expiry_date = models.DateField(verbose_name="تاریخ انقضا") | |
def __str__(self): | |
return str(self.key) | |
# ADDED: MarzbanAdmin model (was missing) | |
class MarzbanAdmin(models.Model): | |
username = models.CharField(max_length=255, unique=True) | |
password = models.CharField(max_length=255) | |
permission = models.CharField(max_length=50) | |
panel = models.ForeignKey(Panel, on_delete=models.CASCADE, related_name='marzban_admins') | |
def __str__(self): | |
return self.username | |
# ADDED: MarzbanUser model (was missing) | |
class MarzbanUser(models.Model): | |
username = models.CharField(max_length=255, unique=True) | |
# Add other fields relevant to Marzban users | |
# ... | |
def __str__(self): | |
return self.username | |
class EndUser(models.Model): | |
username = models.CharField(max_length=255, unique=True) | |
panel = models.ForeignKey(Panel, on_delete=models.CASCADE) | |
# marzban_user_id can be nullable if the user is created first in our DB | |
marzban_user_id = models.CharField(max_length=255, unique=True, null=True, blank=True) | |
telegram_chat_id = models.CharField(max_length=255, blank=True, null=True) | |
def __str__(self): | |
return self.username | |
# ADDED: Plan model (was missing) | |
class Plan(models.Model): | |
name = models.CharField(max_length=100) | |
price = models.DecimalField(max_digits=10, decimal_places=2) | |
duration_days = models.IntegerField() | |
data_limit_gb = models.FloatField() | |
is_active = models.BooleanField(default=True) | |
panel = models.ForeignKey(Panel, on_delete=models.CASCADE, related_name='plans') | |
def __str__(self): | |
return f"{self.name} ({self.panel.name})" | |
# ADDED: Subscription model (was missing) | |
class Subscription(models.Model): | |
STATUS_CHOICES = [ | |
('active', 'فعال'), | |
('expired', 'منقضی شده'), | |
('pending', 'در انتظار پرداخت'), | |
('deactivated', 'غیرفعال'), | |
] | |
end_user = models.ForeignKey(EndUser, on_delete=models.CASCADE, related_name='subscriptions') | |
plan = models.ForeignKey(Plan, on_delete=models.PROTECT) # Don't delete plan if subscription exists | |
panel = models.ForeignKey(Panel, on_delete=models.CASCADE, related_name='subscriptions') | |
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending') | |
start_date = models.DateField(null=True, blank=True) | |
end_date = models.DateField(null=True, blank=True) | |
remaining_data_gb = models.FloatField(null=True, blank=True) | |
def __str__(self): | |
return f"Subscription for {self.end_user.username} on plan {self.plan.name}" | |
# FIXED: Complete overhaul of the Payment model | |
class Payment(models.Model): | |
STATUS_CHOICES = [ | |
('pending', 'در انتظار تأیید'), | |
('approved', 'تأیید شده'), | |
('rejected', 'رد شده'), | |
] | |
subscription = models.ForeignKey(Subscription, on_delete=models.CASCADE, related_name='payments') | |
admin = models.ForeignKey(CustomUser, on_delete=models.PROTECT, related_name='handled_payments') # The admin to approve | |
amount = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="مبلغ") | |
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending', verbose_name="وضعیت") | |
created_at = models.DateTimeField(auto_now_add=True, verbose_name="تاریخ ایجاد") | |
# Fields for receipt upload | |
payment_token = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) | |
receipt_image = models.ImageField(upload_to='receipts/', blank=True, null=True, verbose_name="تصویر رسید") | |
receipt_text = models.TextField(blank=True, null=True, verbose_name="متن رسید") | |
def __str__(self): | |
return f"Payment for {self.subscription.end_user.username} - {self.amount} - {self.status}" | |
class DiscountCode(models.Model): | |
code = models.CharField(max_length=50, unique=True) | |
admin = models.ForeignKey(CustomUser, on_delete=models.CASCADE) | |
discount_percentage = models.DecimalField(max_digits=5, decimal_places=2) | |
is_active = models.BooleanField(default=True) | |
def __str__(self): | |
return self.code | |
# --- Telegram Integration --- | |
class TelegramUser(models.Model): | |
admin_id = models.CharField(max_length=255, unique=True, primary_key=True) | |
chat_id = models.CharField(max_length=255, unique=True) | |
username = models.CharField(max_length=255) | |
class Meta: | |
managed = False | |
db_table = 'telegram_users' | |
def __str__(self): | |
return self.username | |
class SecurityToken(models.Model): | |
admin_id = models.CharField(max_length=255, primary_key=True) | |
token = models.CharField(max_length=255, unique=True, default=uuid.uuid4) | |
expiration_date = models.DateTimeField() | |
class Meta: | |
managed = False | |
db_table = 'telegram_tokens' | |
def __str__(self): | |
return f"Token for {self.admin_id}" | |
# --- Push Notifications --- | |
class PushSubscription(models.Model): | |
user = models.ForeignKey( | |
CustomUser, on_delete=models.CASCADE, related_name='push_subscriptions' | |
) | |
subscription_info = models.JSONField() | |
created_at = models.DateTimeField(auto_now_add=True) | |
# --- Payment System Models --- | |
class PaymentMethod(models.Model): | |
METHOD_CHOICES = [ | |
('gateway', 'Bank Gateway'), | |
('crypto', 'Cryptocurrency'), | |
('manual', 'Manual (Card-to-Card)'), | |
] | |
name = models.CharField(max_length=50, choices=METHOD_CHOICES, unique=True, verbose_name="Payment Method Name") | |
is_active = models.BooleanField(default=True, verbose_name="Is Active?") | |
can_be_managed_by_level3 = models.BooleanField(default=False, verbose_name="Can be managed by Admin Level 3?") | |
class Meta: | |
verbose_name = "Payment Method" | |
verbose_name_plural = "Payment Methods" | |
def __str__(self): | |
return self.get_name_display() | |
class PaymentSetting(models.Model): | |
admin_level_3 = models.ForeignKey( | |
CustomUser, | |
on_delete=models.CASCADE, | |
limit_choices_to={'role': 'AdminLevel3'}, | |
related_name='payment_settings' | |
) | |
payment_method = models.ForeignKey(PaymentMethod, on_delete=models.CASCADE) | |
is_active = models.BooleanField(default=False, verbose_name="Is Active for this Admin?") | |
class Meta: | |
unique_together = ('admin_level_3', 'payment_method') | |
verbose_name = "Payment Setting" | |
verbose_name_plural = "Payment Settings" | |
def __str__(self): | |
return f"{self.admin_level_3.username}'s {self.payment_method.get_name_display()} Setting" | |
class PaymentDetail(models.Model): | |
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) | |
admin_level_3 = models.OneToOneField( | |
CustomUser, | |
on_delete=models.CASCADE, | |
limit_choices_to={'role': 'AdminLevel3'}, | |
related_name='payment_details' | |
) | |
card_number = models.CharField(max_length=50, blank=True, null=True) | |
card_holder_name = models.CharField(max_length=255, blank=True, null=True) | |
wallet_address = models.CharField(max_length=255, blank=True, null=True) | |
class Meta: | |
verbose_name = "Payment Detail" | |
verbose_name_plural = "Payment Details" | |
def __str__(self): | |
return f"Payment details for {self.admin_level_3.username}" | |