feature/vendor #1

Merged
mike merged 4 commits from feature/vendor into main 2023-05-30 09:33:44 +00:00
23 changed files with 1831 additions and 484 deletions

View File

@ -1,3 +1,18 @@
from django.contrib import admin
# Register your models here.
from .models import VendorsData, CryptoCoins, VendorsAddresses
class VendorDetails(admin.ModelAdmin):
fields = ["vendor"]
list_display = ("vendorid_id", "vendor", "vendorWebName", "vendorWebAddr", "vendorAddDate", "vendorIsActive")
class CryptoDetails(admin.ModelAdmin):
list_display = ("coinName", "coinSymbol", "coinAddDate", "coinIsActive" )
class VendorAddressDetails(admin.ModelAdmin):
list_display = ("coin", "address", "vendor_id", "vendorid_id", "addrIsActive")
admin.site.register(VendorsData, VendorDetails)
admin.site.register(CryptoCoins, CryptoDetails)
admin.site.register(VendorsAddresses, VendorAddressDetails)

12
base/forms.py Normal file
View File

@ -0,0 +1,12 @@
from django.forms import ModelForm
from .models import VendorsData, VendorsAddresses
class VendorEditForm(ModelForm):
class Meta:
model = VendorsData
fields = ['vendorCoverAmount', 'vendorPaidNotification', 'vendorPayWindow', 'vendorWebName', 'vendorWebAddr', 'vendorNetworkFee']
class VendorAddrAddForm(ModelForm):
class Meta:
model = VendorsAddresses
fields = ['coin', 'address']

629
base/functions.py Normal file
View File

@ -0,0 +1,629 @@
import string, hashlib, binascii, random, socket, base58, sys
from typing import Union
from urllib.parse import urlparse
from ipaddress import ip_address, ip_network, IPv4Address
from base58 import b58decode_check, b58encode_check
from enum import Enum
def vendor_generator(size=6, chars=string.ascii_uppercase + string.digits):
return ''.join(random.choice(chars) for _ in range(size))
def decodeBase58(address):
decoded = base58.b58decode(address).hex()
prefixAndHash = decoded[:len(decoded)-8]
checksum = decoded[len(decoded)-8:]
hash = prefixAndHash
for _ in range(1,3):
hash = hashlib.sha256(binascii.unhexlify(hash)).hexdigest()
if(checksum == hash[:8]):
return True
return False
def decodeMonero(address):
# decode monero address
if len(address) == 95 or len(address) == 106:
# Decode the address from Base58
decoded = decode(address)
# Verify address type byte
if decoded[0:2] not in ["12", "2a", "13"]:
return False
# Verify checksum
# later
return True
return False
def checksumCheck(method, address):
match method.lower():
case 'btc':
return decodeBase58(address) if address[0] == '1' or address[0] == '3' else True if address[0:3] == 'bc1' and decode("bc", address)[0] != None else False
case 'btct':
return decodeBase58(address) if address[0] == '2' else True if address[0:3] == 'tb1' and decode("tb", address)[0] != None else False
case 'ltc':
return decodeBase58(address) if address[0] == '3' or address[0] == 'M' or address[0] == 'L' else True if address[0:4] == 'ltc1' and decode("ltc", address)[0] != None else False
case 'bch':
return is_valid(address) if address[0] == '1' else True if is_valid('bitcoincash:'+address) == True else False
case 'zec':
return decodeBase58(address) if address[0] == 't' or address[0] == 'z' else False
case 'xmr':
#needs new function to check if address is valid, a decoder maybe
return decodeMonero(address)
case _:
return False
#########################################################################
## UrlValidator
#########################################################################
class UrlValidator:
@staticmethod
def is_internal_address(ip: Union[IPv4Address]) -> bool:
return any([
ip.is_private,
ip.is_unspecified,
ip.is_reserved,
ip.is_loopback,
ip.is_multicast,
ip.is_link_local,
])
@classmethod
def validate(cls, url: str):
DEFAULT_PORT_WHITELIST = {80, 81, 8080, 443, 8443, 8000}
DEFAULT_SCHEME_WHITELIST = {'http', 'https'}
DEFAULT_HOST_BLACKLIST = {'192.0.0.192', '169.254.169.254', '100.100.100.200', 'metadata.packet.net', 'metadata.google.internal'}
DEFAULT_CHARACTER_WHITELIST = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789:/-_.?&='
if url is None:
return False
whitelist_set = set(DEFAULT_CHARACTER_WHITELIST)
if any(c not in whitelist_set for c in url):
return False
try:
ip = ip_address(url)
except ValueError:
try:
host = urlparse(url).hostname
ip = ip_address(str(socket.gethostbyname(host)))
except:
return False
port_whitelist = DEFAULT_PORT_WHITELIST.copy()
scheme_whitelist = DEFAULT_SCHEME_WHITELIST.copy()
host_blacklist = DEFAULT_HOST_BLACKLIST.copy()
try:
port, scheme = urlparse(url).port, urlparse(url).scheme
except:
return False
if scheme_whitelist and scheme is not None and scheme not in scheme_whitelist:
return False
if host_blacklist and host is not None and host in host_blacklist:
return False
if port_whitelist and port is not None and port not in port_whitelist:
return False
if ip.version == 4:
if not ip.is_private:
# CGNAT IPs do not set `is_private` so `not is_global` added
if not ip_network(ip).is_global:
return False
else:
return False
if cls.is_internal_address(ip):
return False
return True
"""Reference implementation for Bech32/Bech32m and segwit addresses."""
#########################################################################
## SEGWIT
#########################################################################
class Encoding(Enum):
"""Enumeration type to list the various supported encodings."""
BECH32 = 1
BECH32M = 2
CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
BECH32M_CONST = 0x2bc830a3
def bech32_polymod(values):
"""Internal function that computes the Bech32 checksum."""
generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]
chk = 1
for value in values:
top = chk >> 25
chk = (chk & 0x1ffffff) << 5 ^ value
for i in range(5):
chk ^= generator[i] if ((top >> i) & 1) else 0
return chk
def bech32_hrp_expand(hrp):
"""Expand the HRP into values for checksum computation."""
return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp]
def bech32_verify_checksum(hrp, data):
"""Verify a checksum given HRP and converted data characters."""
const = bech32_polymod(bech32_hrp_expand(hrp) + data)
if const == 1:
return Encoding.BECH32
if const == BECH32M_CONST:
return Encoding.BECH32M
return None
def bech32_create_checksum(hrp, data, spec):
"""Compute the checksum values given HRP and data."""
values = bech32_hrp_expand(hrp) + data
const = BECH32M_CONST if spec == Encoding.BECH32M else 1
polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ const
return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)]
def bech32_encode(hrp, data, spec):
"""Compute a Bech32 string given HRP and data values."""
combined = data + bech32_create_checksum(hrp, data, spec)
return hrp + '1' + ''.join([CHARSET[d] for d in combined])
def bech32_decode(bech):
"""Validate a Bech32/Bech32m string, and determine HRP and data."""
if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or
(bech.lower() != bech and bech.upper() != bech)):
return (None, None, None)
bech = bech.lower()
pos = bech.rfind('1')
if pos < 1 or pos + 7 > len(bech) or len(bech) > 90:
return (None, None, None)
if not all(x in CHARSET for x in bech[pos+1:]):
return (None, None, None)
hrp = bech[:pos]
data = [CHARSET.find(x) for x in bech[pos+1:]]
spec = bech32_verify_checksum(hrp, data)
if spec is None:
return (None, None, None)
return (hrp, data[:-6], spec)
def convertbits(data, frombits, tobits, pad=True):
"""General power-of-2 base conversion."""
acc = 0
bits = 0
ret = []
maxv = (1 << tobits) - 1
max_acc = (1 << (frombits + tobits - 1)) - 1
for value in data:
if value < 0 or (value >> frombits):
return None
acc = ((acc << frombits) | value) & max_acc
bits += frombits
while bits >= tobits:
bits -= tobits
ret.append((acc >> bits) & maxv)
if pad:
if bits:
ret.append((acc << (tobits - bits)) & maxv)
elif bits >= frombits or ((acc << (tobits - bits)) & maxv):
return None
return ret
def decode(hrp, addr):
"""Decode a segwit address."""
hrpgot, data, spec = bech32_decode(addr)
if hrpgot != hrp:
return (None, None)
decoded = convertbits(data[1:], 5, 8, False)
if decoded is None or len(decoded) < 2 or len(decoded) > 40:
return (None, None)
if data[0] > 16:
return (None, None)
if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32:
return (None, None)
if data[0] == 0 and spec != Encoding.BECH32 or data[0] != 0 and spec != Encoding.BECH32M:
return (None, None)
return (data[0], decoded)
def encode(hrp, witver, witprog):
"""Encode a segwit address."""
spec = Encoding.BECH32 if witver == 0 else Encoding.BECH32M
ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5), spec)
if decode(hrp, ret) == (None, None):
return None
return ret
#########################################################################
## CRYPTO
#########################################################################
CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'
def polymod(values):
chk = 1
generator = [
(0x01, 0x98f2bc8e61),
(0x02, 0x79b76d99e2),
(0x04, 0xf33e5fb3c4),
(0x08, 0xae2eabe2a8),
(0x10, 0x1e4f43e470)]
for value in values:
top = chk >> 35
chk = ((chk & 0x07ffffffff) << 5) ^ value
for i in generator:
if top & i[0] != 0:
chk ^= i[1]
return chk ^ 1
def prefix_expand(prefix):
return [ord(x) & 0x1f for x in prefix] + [0]
def calculate_checksum(prefix, payload):
poly = polymod(prefix_expand(prefix) + payload + [0, 0, 0, 0, 0, 0, 0, 0])
out = list()
for i in range(8):
out.append((poly >> 5 * (7 - i)) & 0x1f)
return out
def verify_checksum(prefix, payload):
return polymod(prefix_expand(prefix) + payload) == 0
def b32decode(inputs):
out = list()
for letter in inputs:
out.append(CHARSET.find(letter))
return out
def b32encode(inputs):
out = ''
for char_code in inputs:
out += CHARSET[char_code]
return out
def convertbits(data, frombits, tobits, pad=True):
acc = 0
bits = 0
ret = []
maxv = (1 << tobits) - 1
max_acc = (1 << (frombits + tobits - 1)) - 1
for value in data:
if value < 0 or (value >> frombits):
return None
acc = ((acc << frombits) | value) & max_acc
bits += frombits
while bits >= tobits:
bits -= tobits
ret.append((acc >> bits) & maxv)
if pad:
if bits:
ret.append((acc << (tobits - bits)) & maxv)
elif bits >= frombits or ((acc << (tobits - bits)) & maxv):
return None
return ret
#########################################################################
## BCH CONVERT
#########################################################################
class InvalidAddress(Exception):
pass
class Address:
VERSION_MAP = {
'legacy': [
('P2SH', 5, False),
('P2PKH', 0, False),
('P2SH-TESTNET', 196, True),
('P2PKH-TESTNET', 111, True)
],
'cash': [
('P2SH', 8, False),
('P2PKH', 0, False),
('P2SH-TESTNET', 8, True),
('P2PKH-TESTNET', 0, True)
]
}
MAINNET_PREFIX = 'bitcoincash'
TESTNET_PREFIX = 'bchtest'
def __init__(self, version, payload, prefix=None):
self.version = version
self.payload = payload
if prefix:
self.prefix = prefix
else:
if Address._address_type('cash', self.version)[2]:
self.prefix = self.TESTNET_PREFIX
else:
self.prefix = self.MAINNET_PREFIX
def __str__(self):
return 'version: {}\npayload: {}\nprefix: {}'.format(self.version, self.payload, self.prefix)
def legacy_address(self):
version_int = Address._address_type('legacy', self.version)[1]
return b58encode_check(Address.code_list_to_string([version_int] + self.payload))
def cash_address(self):
version_int = Address._address_type('cash', self.version)[1]
payload = [version_int] + self.payload
payload = convertbits(payload, 8, 5)
checksum = calculate_checksum(self.prefix, payload)
return self.prefix + ':' + b32encode(payload + checksum)
@staticmethod
def code_list_to_string(code_list):
if sys.version_info > (3, 0):
output = bytes()
for code in code_list:
output += bytes([code])
else:
output = ''
for code in code_list:
output += chr(code)
return output
@staticmethod
def _address_type(address_type, version):
for mapping in Address.VERSION_MAP[address_type]:
if mapping[0] == version or mapping[1] == version:
return mapping
raise InvalidAddress('Could not determine address version')
@staticmethod
def from_string(address_string):
try:
address_string = str(address_string)
except Exception:
raise InvalidAddress('Expected string as input')
if ':' not in address_string:
return Address._legacy_string(address_string)
else:
return Address._cash_string(address_string)
@staticmethod
def _legacy_string(address_string):
try:
decoded = bytearray(b58decode_check(address_string))
except ValueError:
raise InvalidAddress('Could not decode legacy address')
version = Address._address_type('legacy', decoded[0])[0]
payload = list()
for letter in decoded[1:]:
payload.append(letter)
return Address(version, payload)
@staticmethod
def _cash_string(address_string):
if address_string.upper() != address_string and address_string.lower() != address_string:
raise InvalidAddress('Cash address contains uppercase and lowercase characters')
address_string = address_string.lower()
colon_count = address_string.count(':')
if colon_count == 0:
address_string = Address.MAINNET_PREFIX + ':' + address_string
elif colon_count > 1:
raise InvalidAddress('Cash address contains more than one colon character')
prefix, base32string = address_string.split(':')
decoded = b32decode(base32string)
if not verify_checksum(prefix, decoded):
raise InvalidAddress('Bad cash address checksum')
converted = convertbits(decoded, 5, 8)
version = Address._address_type('cash', converted[0])[0]
if prefix == Address.TESTNET_PREFIX:
version += '-TESTNET'
payload = converted[1:-6]
return Address(version, payload, prefix)
def to_cash_address(address):
return Address.from_string(address).cash_address()
def to_legacy_address(address):
return Address.from_string(address).legacy_address()
def is_valid(address):
try:
Address.from_string(address)
return True
except InvalidAddress:
return False
#########################################################################
## monero
#########################################################################
__alphabet = [
ord(s) for s in "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
]
__b58base = 58
__UINT64MAX = 2 ** 64
__encodedBlockSizes = [0, 2, 3, 5, 6, 7, 9, 10, 11]
__fullBlockSize = 8
__fullEncodedBlockSize = 11
def _hexToBin(hex_):
if len(hex_) % 2 != 0:
raise ValueError("Hex string has invalid length: %d" % len(hex_))
return [int(hex_[i : i + 2], 16) for i in range(0, len(hex_), 2)]
def _binToHex(bin_):
return "".join("%02x" % int(b) for b in bin_)
def _uint8be_to_64(data):
if not (1 <= len(data) <= 8):
raise ValueError("Invalid input length: %d" % len(data))
res = 0
for b in data:
res = res << 8 | b
return res
def _uint64_to_8be(num, size):
if size < 1 or size > 8:
raise ValueError("Invalid input length: %d" % size)
res = [0] * size
twopow8 = 2 ** 8
for i in range(size - 1, -1, -1):
res[i] = num % twopow8
num = num // twopow8
return res
def encode_block(data, buf, index):
l_data = len(data)
if l_data < 1 or l_data > __fullEncodedBlockSize:
raise ValueError("Invalid block length: %d" % l_data)
num = _uint8be_to_64(data)
i = __encodedBlockSizes[l_data] - 1
while num > 0:
remainder = num % __b58base
num = num // __b58base
buf[index + i] = __alphabet[remainder]
i -= 1
return buf
def encode(hex):
"""Encode hexadecimal string as base58 (ex: encoding a Monero address)."""
data = _hexToBin(hex)
l_data = len(data)
if l_data == 0:
return ""
full_block_count = l_data // __fullBlockSize
last_block_size = l_data % __fullBlockSize
res_size = (
full_block_count * __fullEncodedBlockSize + __encodedBlockSizes[last_block_size]
)
res = bytearray([__alphabet[0]] * res_size)
for i in range(full_block_count):
res = encode_block(
data[(i * __fullBlockSize) : (i * __fullBlockSize + __fullBlockSize)],
res,
i * __fullEncodedBlockSize,
)
if last_block_size > 0:
res = encode_block(
data[
(full_block_count * __fullBlockSize) : (
full_block_count * __fullBlockSize + last_block_size
)
],
res,
full_block_count * __fullEncodedBlockSize,
)
return bytes(res).decode("ascii")
def decode_block(data, buf, index):
l_data = len(data)
if l_data < 1 or l_data > __fullEncodedBlockSize:
raise ValueError("Invalid block length: %d" % l_data)
res_size = __encodedBlockSizes.index(l_data)
if res_size <= 0:
raise ValueError("Invalid block size: %d" % res_size)
res_num = 0
order = 1
for i in range(l_data - 1, -1, -1):
digit = __alphabet.index(data[i])
if digit < 0:
raise ValueError("Invalid symbol: %s" % data[i])
product = order * digit + res_num
if product > __UINT64MAX:
raise ValueError(
"Overflow: %d * %d + %d = %d" % (order, digit, res_num, product)
)
res_num = product
order = order * __b58base
if res_size < __fullBlockSize and 2 ** (8 * res_size) <= res_num:
raise ValueError("Overflow: %d doesn't fit in %d bit(s)" % (res_num, res_size))
tmp_buf = _uint64_to_8be(res_num, res_size)
buf[index : index + len(tmp_buf)] = tmp_buf
return buf
def decode(enc):
"""Decode a base58 string (ex: a Monero address) into hexidecimal form."""
enc = bytearray(enc, encoding="ascii")
l_enc = len(enc)
if l_enc == 0:
return ""
full_block_count = l_enc // __fullEncodedBlockSize
last_block_size = l_enc % __fullEncodedBlockSize
try:
last_block_decoded_size = __encodedBlockSizes.index(last_block_size)
except ValueError:
raise ValueError("Invalid encoded length: %d" % l_enc)
data_size = full_block_count * __fullBlockSize + last_block_decoded_size
data = bytearray(data_size)
for i in range(full_block_count):
data = decode_block(
enc[
(i * __fullEncodedBlockSize) : (
i * __fullEncodedBlockSize + __fullEncodedBlockSize
)
],
data,
i * __fullBlockSize,
)
if last_block_size > 0:
data = decode_block(
enc[
(full_block_count * __fullEncodedBlockSize) : (
full_block_count * __fullEncodedBlockSize + last_block_size
)
],
data,
full_block_count * __fullBlockSize,
)
return _binToHex(data)
#########################################################################
#########################################################################

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2 on 2023-04-29 14:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('base', '0004_vendorsdata_vendoruuid'),
]
operations = [
migrations.AlterField(
model_name='vendorsdata',
name='vendorIsActive',
field=models.BooleanField(default=1),
),
]

View File

@ -0,0 +1,33 @@
# Generated by Django 4.2 on 2023-04-29 14:02
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('base', '0005_alter_vendorsdata_vendorisactive'),
]
operations = [
migrations.AlterField(
model_name='vendorsdata',
name='vendorDeleted',
field=models.BooleanField(default=False),
),
migrations.AlterField(
model_name='vendorsdata',
name='vendorPaidNotification',
field=models.BooleanField(default=True),
),
migrations.AlterField(
model_name='vendorsdata',
name='vendorPayWindow',
field=models.BooleanField(default=True),
),
migrations.AlterField(
model_name='vendorsdata',
name='vendorSkipScreen',
field=models.BooleanField(default=False),
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 4.2 on 2023-04-29 17:46
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('base', '0006_alter_vendorsdata_vendordeleted_and_more'),
]
operations = [
migrations.AddField(
model_name='vendorsdata',
name='vendorUpdated',
field=models.DateTimeField(auto_now=True),
),
migrations.AlterField(
model_name='vendorsdata',
name='vendorAddDelete',
field=models.DateTimeField(auto_now_add=True),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2 on 2023-05-02 12:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('base', '0007_vendorsdata_vendorupdated_and_more'),
]
operations = [
migrations.AlterField(
model_name='vendorsdata',
name='vendorWebAddr',
field=models.URLField(max_length=250),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2 on 2023-05-02 15:16
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('base', '0008_alter_vendorsdata_vendorwebaddr'),
]
operations = [
migrations.AlterField(
model_name='vendorsdata',
name='id',
field=models.BigAutoField(primary_key=True, serialize=False),
),
]

View File

@ -1,30 +1,74 @@
from django.db import models
from users.models import User
from django.contrib import admin
# Create your models here.
class CryptoCoins(models.Model):
id = models.BigAutoField(primary_key=True)
coinName = models.CharField(max_length=250)
coinSymbol = models.CharField(max_length=10)
coinIsActive = models.BooleanField(default=True)
coinAddDate = models.DateTimeField(auto_now_add=True)
coinUpdated = models.DateTimeField(auto_now=True)
coinDelete = models.DateTimeField(auto_now_add=True, blank=True, null=True)
coinDeleted = models.BooleanField(default=False)
class Meta:
verbose_name = "Crypto Coin"
verbose_name_plural = "Crypto Coins"
ordering = ["-id"]
class VendorsData(models.Model):
id = models.BigAutoField(primary_key=True)
vendorid = models.ForeignKey(User, default=None, on_delete=models.CASCADE)
vendorUUID = models.UUIDField()
vendor = models.CharField(max_length=8, unique=True)
vendorSecretKey = models.UUIDField()
vendorSecretKey = models.CharField(max_length=250)
vendorCoverAmount = models.IntegerField(default=0)
vendorPaidNotification = models.IntegerField(default=1)
vendorSkipScreen = models.IntegerField(default=0)
vendorPayWindow = models.IntegerField(default=0)
vendorNetworkFee = models.IntegerField(default=0)
vendorPaidNotification = models.BooleanField(default=True)
vendorSkipScreen = models.BooleanField(default=False)
vendorPayWindow = models.BooleanField(default=True)
vendorWebName = models.CharField(max_length=250)
vendorWebAddr = models.CharField(max_length=250)
vendorWebAddr = models.URLField(max_length=250)
vendorAddDate = models.DateTimeField(auto_now_add=True)
vendorDeleted = models.IntegerField(default=0)
vendorAddDelete = models.DateTimeField(auto_now=True)
vendorIsActive = models.IntegerField(default=1)
vendorDeleted = models.BooleanField(default=False)
vendorUpdated = models.DateTimeField(auto_now=True)
vendorAddDelete = models.DateTimeField(auto_now_add=True)
vendorIsActive = models.BooleanField(default=True)
def __str__(self):
return self.vendor
def __repr__(self):
return self._repr()
class Meta:
verbose_name = "Vendor"
verbose_name_plural = "Vendors"
ordering = ["-id"]
class VendorsAddresses(models.Model):
id = models.BigAutoField(primary_key=True)
vendor = models.ForeignKey(VendorsData, default=None, on_delete=models.CASCADE)
vendorid = models.ForeignKey(User, default=None, on_delete=models.CASCADE)
vendorUUID = models.UUIDField()
coin = models.CharField(max_length=10)
address = models.CharField(max_length=250)
addrAddDate = models.DateTimeField(auto_now_add=True)
addrDeleted = models.BooleanField(default=False)
addrUpdated = models.DateTimeField(auto_now=True)
addrIsActive = models.BooleanField(default=True)
def __str__(self):
return self.address
class Meta:
verbose_name = "Address"
verbose_name_plural = "Addresses"
ordering = ["-addrAddDate"]

View File

@ -7,321 +7,349 @@
<!-- ============================================================== -->
<!-- Main content -->
<div class="h-screen flex-grow-1 overflow-y-lg-auto">
<!-- Header -->
<header class="bg-surface-primary border-bottom pt-6">
<div class="container-fluid">
<div class="mb-npx">
<div class="row align-items-center">
<div class="col-sm-6 col-12 mb-4 mb-sm-0">
<!-- Title -->
<h1 class="h2 mb-0 ls-tight"> Dashboard </h1>
<hr class="navbar-divider my-1 opacity-20">
<p> Hello "user", welcome! Here's what's happening with your store today.</p>
</div>
<!-- Actions -->
<hr class="navbar-divider my-1 opacity-20">
</div>
<!-- Nav -->
</div>
</div>
</header>
<!-- Main -->
<main class="py-6 bg-surface-secondary">
<div class="container-fluid">
<!-- Card stats -->
<div class="row g-6 mb-6">
<div class="col-xl-3 col-sm-6 col-12">
<div class="card shadow border-0">
<div class="card-body">
<div class="row">
<div class="flex-fill overflow-y-lg-auto scrollbar bg-surface-primary rounded-top-4 rounded-top-start-lg-4 rounded-top-end-lg-0 border-top border-lg shadow">
<main class="container-fluid px-3 py-5 p-lg-6 p-xxl-10">
<div class="mb-6 mb-xl-10">
<div class="row g-3 align-items-center">
<div class="col">
<span class="h6 font-semibold text-muted text-sm d-block mb-2">Earnings</span>
<span class="h3 font-bold mb-0">$750.90</span>
<h1 class="h2 ls-tight">Dashboard</h1>
</div>
<div class="col-auto">
<div class="icon icon-shape bg-tertiary text-white text-lg rounded-circle">
<i class="bi bi-credit-card"></i>
</div>
</div>
<div class="row g-3 g-xxl-6">
<div class="col-xxl-12">
<div class="vstack gap-3 gap-md-6">
<div class="row g-3">
<div class="col-md col-sm-6">
<div class="card bg-success bg-opacity-10 border-success border-opacity-40">
<div class="card-body p-4">
<div class="d-flex align-items-center gap-2">
<img class="w-5 flex-none" src="{% static '/img/crypto/color/usdt.svg' %}" alt="..." />
<h6>USDT</h6>
</div>
<div class="text-xs mt-4">
Total balance
</div>
<div class="text-sm font-semibold">
75.800,00 USDT
</div>
</div>
</div>
<div class="mt-2 mb-0 text-sm">
<span class="badge badge-pill bg-soft-success text-success me-2">
<i class="bi bi-arrow-up me-1"></i>13%
</div>
<div class="col-md col-sm-6">
<div class="card border-primary-hover">
<div class="card-body p-4">
<div class="d-flex align-items-center gap-2">
<img class="w-5 flex-none" src="{% static '/img/crypto/color/btc.svg' %}" alt="..." />
<a href="/pages/exchange-details.html" class="h6 stretched-link">BTC</a>
</div>
<div class="text-sm font-semibold mt-3">
3.2893 USDT
</div>
<div class="d-flex align-items-center gap-2 mt-1 text-xs">
<span class="badge badge-xs rounded-pill text-bg-success">
<i class="bi bi-arrow-up-right"></i>
</span>
<span class="text-nowrap text-xs text-muted">Since last month</span>
<span>+13.7%</span>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-sm-6 col-12">
<div class="card shadow border-0">
<div class="card-body">
<div class="row">
<div class="col">
<span class="h6 font-semibold text-muted text-sm d-block mb-2">Balance</span>
<span class="h3 font-bold mb-0">215</span>
<div class="col-md col-sm-6">
<div class="card border-primary-hover">
<div class="card-body p-4">
<div class="d-flex align-items-center gap-2">
<img class="w-5 flex-none" src="{% static '/img/crypto/color/ada.svg' %}" alt="..." />
<a href="/pages/exchange-details.html" class="h6 stretched-link">ADA</a>
</div>
<div class="col-auto">
<div class="icon icon-shape bg-primary text-white text-lg rounded-circle">
<i class="bi bi-people"></i>
<div class="text-sm font-semibold mt-3">
10.745,49 ADA
</div>
</div>
</div>
<div class="mt-2 mb-0 text-sm">
<span class="badge badge-pill bg-soft-success text-success me-2">
<i class="bi bi-arrow-up me-1"></i>30%
<div class="d-flex align-items-center gap-2 mt-1 text-xs">
<span class="badge badge-xs rounded-pill text-bg-danger">
<i class="bi bi-arrow-up-right"></i>
</span>
<span class="text-nowrap text-xs text-muted">Since last month</span>
<span>-3.2%</span>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-sm-6 col-12">
<div class="card shadow border-0">
<div class="card-body">
<div class="row">
<div class="col">
<span class="h6 font-semibold text-muted text-sm d-block mb-2">Customers</span>
<span class="h3 font-bold mb-0">1.400</span>
<div class="col-md col-sm-6">
<div class="card border-primary-hover">
<div class="card-body p-4">
<div class="d-flex align-items-center gap-2">
<img class="w-5 flex-none" src="{% static '/img/crypto/color/eos.svg' %}" alt="..." />
<a href="/pages/exchange-details.html" class="h6 stretched-link">EOS</a>
</div>
<div class="col-auto">
<div class="icon icon-shape bg-info text-white text-lg rounded-circle">
<i class="bi bi-clock-history"></i>
<div class="text-sm font-semibold mt-3">
7.890,00 EOS
</div>
</div>
</div>
<div class="mt-2 mb-0 text-sm">
<span class="badge badge-pill bg-soft-danger text-danger me-2">
<i class="bi bi-arrow-down me-1"></i>-5%
<div class="d-flex align-items-center gap-2 mt-1 text-xs">
<span class="badge badge-xs rounded-pill text-bg-danger">
<i class="bi bi-arrow-up-right"></i>
</span>
<span class="text-nowrap text-xs text-muted">Since last month</span>
<span>-2.2%</span>
</div>
</div>
</div>
</div>
<div class="col-xl-3 col-sm-6 col-12">
<div class="card shadow border-0">
<div class="card-body">
<div class="row">
<div class="col">
<span class="h6 font-semibold text-muted text-sm d-block mb-2">Invoices</span>
<span class="h3 font-bold mb-0">150</span>
</div>
<div class="col-auto">
<div class="icon icon-shape bg-warning text-white text-lg rounded-circle">
<i class="bi bi-minecart-loaded"></i>
<div class="col-md-1 d-none d-md-block">
<div class="card h-md-full d-flex flex-column align-items-center justify-content-center py-4 bg-light bg-opacity-80 bg-opacity-100-hover">
<a href="#cryptoModal" class="stretched-link text-dark" data-bs-toggle="modal">
<i class="bi bi-plus-lg"></i>
</a>
</div>
</div>
</div>
<div class="mt-2 mb-0 text-sm">
<span class="badge badge-pill bg-soft-success text-success me-2">
<i class="bi bi-arrow-up me-1"></i>10%
<div class="card">
<div class="card-body pb-0">
<div class="d-flex justify-content-between align-items-center">
<div>
<h5>Transaction History</h5>
</div>
<div class="hstack align-items-center">
<a href="#" class="text-muted">
<i class="bi bi-arrow-repeat"></i>
</a>
</div>
</div>
<div class="list-group list-group-flush">
<div class="list-group-item d-flex align-items-center justify-content-between gap-6">
<div class="d-flex align-items-center gap-3">
<div class="icon icon-shape rounded-circle icon-sm flex-none w-10 h-10 text-sm bg-primary bg-opacity-20 text-primary">
<i class="bi bi-send-fill"></i>
</div>
<div class="">
<span class="d-block text-heading text-sm font-semibold">
Bitcoin
</span>
<span class="text-nowrap text-xs text-muted">Since last month</span>
<span class="d-none d-sm-block text-muted text-xs">
2 days ago
</span>
</div>
</div>
<div class="d-none d-md-block text-sm">
0xd029384sd343fd...eq23
</div>
<div class="d-none d-md-block">
<span class="badge bg-light text-warning rounded-pill">
Pending
</span>
</div>
<div class="text-end">
<span class="d-block text-heading text-sm font-bold">
+0.2948 BTC
</span>
<span class="d-block text-muted text-xs">
+$10,930.90
</span>
</div>
</div>
<div class="list-group-item d-flex align-items-center justify-content-between gap-6">
<div class="d-flex align-items-center gap-3">
<div class="icon icon-shape rounded-circle icon-sm flex-none w-10 h-10 text-sm bg-primary bg-opacity-20 text-primary">
<i class="bi bi-send-fill"></i>
</div>
<div class="">
<span class="d-block text-heading text-sm font-semibold">
Cardano
</span>
<span class="d-none d-sm-block text-muted text-xs">
2 days ago
</span>
</div>
</div>
<div class="d-none d-md-block text-sm">
0xd029384sd343fd...eq23
</div>
<div class="d-none d-md-block">
<span class="badge bg-light text-danger rounded-pill">
Canceled
</span>
</div>
<div class="text-end">
<span class="d-block text-heading text-sm font-bold">
+0.2948 BTC
</span>
<span class="d-block text-muted text-xs">
+$10,930.90
</span>
</div>
</div>
<div class="list-group-item d-flex align-items-center justify-content-between gap-6">
<div class="d-flex align-items-center gap-3">
<div class="icon icon-shape rounded-circle icon-sm flex-none w-10 h-10 text-sm bg-primary bg-opacity-20 text-primary">
<i class="bi bi-send-fill"></i>
</div>
<div class="">
<span class="d-block text-heading text-sm font-semibold">
Binance
</span>
<span class="d-none d-sm-block text-muted text-xs">
2 days ago
</span>
</div>
</div>
<div class="d-none d-md-block text-sm">
0xd029384sd343fd...eq23
</div>
<div class="d-none d-md-block">
<span class="badge bg-light text-success rounded-pill">
Successful
</span>
</div>
<div class="text-end">
<span class="d-block text-heading text-sm font-bold">
+0.2948 BTC
</span>
<span class="d-block text-muted text-xs">
+$10,930.90
</span>
</div>
</div>
<div class="list-group-item d-flex align-items-center justify-content-between gap-6">
<div class="d-flex align-items-center gap-3">
<div class="icon icon-shape rounded-circle icon-sm flex-none w-10 h-10 text-sm bg-primary bg-opacity-20 text-primary">
<i class="bi bi-send-fill"></i>
</div>
<div class="">
<span class="d-block text-heading text-sm font-semibold">
Bitcoin
</span>
<span class="d-none d-sm-block text-muted text-xs">
2 days ago
</span>
</div>
</div>
<div class="d-none d-md-block text-sm">
0xd029384sd343fd...eq23
</div>
<div class="d-none d-md-block">
<span class="badge bg-light text-warning rounded-pill">
Pending
</span>
</div>
<div class="text-end">
<span class="d-block text-heading text-sm font-bold">
+0.2948 BTC
</span>
<span class="d-block text-muted text-xs">
+$10,930.90
</span>
</div>
</div>
<div class="list-group-item d-flex align-items-center justify-content-between gap-6">
<div class="d-flex align-items-center gap-3">
<div class="icon icon-shape rounded-circle icon-sm flex-none w-10 h-10 text-sm bg-primary bg-opacity-20 text-primary">
<i class="bi bi-send-fill"></i>
</div>
<div class="">
<span class="d-block text-heading text-sm font-semibold">
Bitcoin
</span>
<span class="d-none d-sm-block text-muted text-xs">
2 days ago
</span>
</div>
</div>
<div class="d-none d-md-block text-sm">
0xd029384sd343fd...eq23
</div>
<div class="d-none d-md-block">
<span class="badge bg-light text-danger rounded-pill">
Canceled
</span>
</div>
<div class="text-end">
<span class="d-block text-heading text-sm font-bold">
+0.2948 BTC
</span>
<span class="d-block text-muted text-xs">
+$10,930.90
</span>
</div>
</div>
<div class="list-group-item d-flex align-items-center justify-content-between gap-6">
<div class="d-flex align-items-center gap-3">
<div class="icon icon-shape rounded-circle icon-sm flex-none w-10 h-10 text-sm bg-primary bg-opacity-20 text-primary">
<i class="bi bi-send-fill"></i>
</div>
<div class="">
<span class="d-block text-heading text-sm font-semibold">
Bitcoin
</span>
<span class="d-none d-sm-block text-muted text-xs">
2 days ago
</span>
</div>
</div>
<div class="d-none d-md-block text-sm">
0xd029384sd343fd...eq23
</div>
<div class="d-none d-md-block">
<span class="badge bg-light text-success rounded-pill">
Successful
</span>
</div>
<div class="text-end">
<span class="d-block text-heading text-sm font-bold">
+0.2948 BTC
</span>
<span class="d-block text-muted text-xs">
+$10,930.90
</span>
</div>
</div>
<div class="list-group-item d-flex align-items-center justify-content-between gap-6">
<div class="d-flex align-items-center gap-3">
<div class="icon icon-shape rounded-circle icon-sm flex-none w-10 h-10 text-sm bg-primary bg-opacity-20 text-primary">
<i class="bi bi-send-fill"></i>
</div>
<div class="">
<span class="d-block text-heading text-sm font-semibold">
Bitcoin
</span>
<span class="d-none d-sm-block text-muted text-xs">
2 days ago
</span>
</div>
</div>
<div class="d-none d-md-block text-sm">
0xd029384sd343fd...eq23
</div>
<div class="d-none d-md-block">
<span class="badge bg-light text-success rounded-pill">
Successful
</span>
</div>
<div class="text-end">
<span class="d-block text-heading text-sm font-bold">
+0.2948 BTC
</span>
<span class="d-block text-muted text-xs">
+$10,930.90
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card shadow border-0 mb-7">
<div class="card-header">
<h5 class="mb-0">Last 5 paid invoices</h5>
</div>
<div class="table-responsive">
<table class="table table-hover table-nowrap">
<thead class="thead-light">
<tr>
<th scope="col">Name</th>
<th scope="col">Date</th>
<th scope="col">Company</th>
<th scope="col">Offer</th>
<th scope="col">Meeting</th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>
<img alt="..." src="https://images.unsplash.com/photo-1502823403499-6ccfcf4fb453?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=3&w=256&h=256&q=80" class="avatar avatar-sm rounded-circle me-2">
<a class="text-heading font-semibold" href="#">
Robert Fox
</a>
</td>
<td>
Feb 15, 2021
</td>
<td>
<img alt="..." src="https://preview.webpixels.io/web/img/other/logos/logo-1.png" class="avatar avatar-xs rounded-circle me-2">
<a class="text-heading font-semibold" href="#">
Dribbble
</a>
</td>
<td>
$3.500
</td>
<td>
<span class="badge badge-lg badge-dot">
<i class="bg-success"></i>Scheduled
</span>
</td>
<td class="text-end">
<a href="#" class="btn btn-sm btn-neutral">View</a>
<button type="button" class="btn btn-sm btn-square btn-neutral text-danger-hover">
<i class="bi bi-trash"></i>
</button>
</td>
</tr>
<tr>
<td>
<img alt="..." src="https://images.unsplash.com/photo-1610271340738-726e199f0258?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=3&w=256&h=256&q=80" class="avatar avatar-sm rounded-circle me-2">
<a class="text-heading font-semibold" href="#">
Darlene Robertson
</a>
</td>
<td>
Apr 15, 2021
</td>
<td>
<img alt="..." src="https://preview.webpixels.io/web/img/other/logos/logo-2.png" class="avatar avatar-xs rounded-circle me-2">
<a class="text-heading font-semibold" href="#">
Netguru
</a>
</td>
<td>
$2.750
</td>
<td>
<span class="badge badge-lg badge-dot">
<i class="bg-warning"></i>Postponed
</span>
</td>
<td class="text-end">
<a href="#" class="btn btn-sm btn-neutral">View</a>
<button type="button" class="btn btn-sm btn-square btn-neutral text-danger-hover">
<i class="bi bi-trash"></i>
</button>
</td>
</tr>
<tr>
<td>
<img alt="..." src="https://images.unsplash.com/photo-1610878722345-79c5eaf6a48c?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=3&w=256&h=256&q=80" class="avatar avatar-sm rounded-circle me-2">
<a class="text-heading font-semibold" href="#">
Theresa Webb
</a>
</td>
<td>
Mar 20, 2021
</td>
<td>
<img alt="..." src="https://preview.webpixels.io/web/img/other/logos/logo-3.png" class="avatar avatar-xs rounded-circle me-2">
<a class="text-heading font-semibold" href="#">
Figma
</a>
</td>
<td>
$4.200
</td>
<td>
<span class="badge badge-lg badge-dot">
<i class="bg-success"></i>Scheduled
</span>
</td>
<td class="text-end">
<a href="#" class="btn btn-sm btn-neutral">View</a>
<button type="button" class="btn btn-sm btn-square btn-neutral text-danger-hover">
<i class="bi bi-trash"></i>
</button>
</td>
</tr>
<tr>
<td>
<img alt="..." src="https://images.unsplash.com/photo-1612422656768-d5e4ec31fac0?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=3&w=256&h=256&q=80" class="avatar avatar-sm rounded-circle me-2">
<a class="text-heading font-semibold" href="#">
Kristin Watson
</a>
</td>
<td>
Feb 15, 2021
</td>
<td>
<img alt="..." src="https://preview.webpixels.io/web/img/other/logos/logo-4.png" class="avatar avatar-xs rounded-circle me-2">
<a class="text-heading font-semibold" href="#">
Mailchimp
</a>
</td>
<td>
$3.500
</td>
<td>
<span class="badge badge-lg badge-dot">
<i class="bg-dark"></i>Not discussed
</span>
</td>
<td class="text-end">
<a href="#" class="btn btn-sm btn-neutral">View</a>
<button type="button" class="btn btn-sm btn-square btn-neutral text-danger-hover">
<i class="bi bi-trash"></i>
</button>
</td>
</tr>
<tr>
<td>
<img alt="..." src="https://images.unsplash.com/photo-1608976328267-e673d3ec06ce?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=3&w=256&h=256&q=80" class="avatar avatar-sm rounded-circle me-2">
<a class="text-heading font-semibold" href="#">
Cody Fisher
</a>
</td>
<td>
Apr 10, 2021
</td>
<td>
<img alt="..." src="https://preview.webpixels.io/web/img/other/logos/logo-5.png" class="avatar avatar-xs rounded-circle me-2">
<a class="text-heading font-semibold" href="#">
Webpixels
</a>
</td>
<td>
$1.500
</td>
<td>
<span class="badge badge-lg badge-dot">
<i class="bg-danger"></i>Canceled
</span>
</td>
<td class="text-end">
<a href="#" class="btn btn-sm btn-neutral">View</a>
<button type="button" class="btn btn-sm btn-square btn-neutral text-danger-hover">
<i class="bi bi-trash"></i>
</button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="card-footer border-0 py-5">
<span class="text-muted text-sm">#</span>
</div>
</div>
</div>
</main>
</div>
</div>
</div>
<!-- Dashboard -->
<!-- End Page-content -->
<div>
{% block footer %}
{% include "partials/footer.html" %}
{% endblock footer %}
</div>
<!-- end main content-->
{% endblock content %}

View File

@ -1,6 +1,6 @@
{% extends "partials/base.html" %}
{% load static %}
{% block title %}Dashboard{% endblock title %}
{% block title %}Vendor Page{% endblock title %}
{% block content %}
<!-- ============================================================== -->
<!-- Start right Content here -->
@ -8,24 +8,7 @@
<!-- Main content -->
<div class="h-screen flex-grow-1 overflow-y-lg-auto">
<!-- Header -->
<header class="bg-surface-primary border-bottom pt-6">
<div class="container-fluid">
<div class="mb-npx">
<div class="row align-items-center">
<div class="col-sm-6 col-12 mb-4 mb-sm-0">
<!-- Title -->
<h1 class="h2 mb-0 ls-tight"> Vendor Page </h1>
<hr class="navbar-divider my-1 opacity-20">
</div>
<!-- Actions -->
<hr class="navbar-divider my-1 opacity-20">
</div>
<!-- Nav -->
</div>
</div>
</header>
<!-- Main -->
<main class="py-6 bg-surface-secondary">
<div class="container-fluid">
@ -39,9 +22,9 @@
<thead class="thead-light">
<tr>
<th scope="col">VendorID</th>
<th scope="col">Creation Date</th>
<th scope="col">Website Name</th>
<th scope="col"></th>
<th scope="col">Created</th>
<th scope="col">Updated</th>
<th scope="col">Status</th>
<th></th>
</tr>
@ -50,22 +33,18 @@
<tr>
<td>
<img alt="..." src="https://preview.webpixels.io/web/img/other/logos/logo-1.png" class="avatar avatar-xs rounded-circle me-2">
<a class="text-heading font-semibold" href="#">
{{vendor.vendor}}
</a>
</td>
<td>
{{vendor.vendorAddDate}}
</td>
<td>
<img alt="..." src="https://preview.webpixels.io/web/img/other/logos/logo-1.png" class="avatar avatar-xs rounded-circle me-2">
<a class="text-heading font-semibold" href="#">
{{vendor.vendorWebName}}
</a>
{{vendor.vendorWebName|truncatechars:20}}
</td>
<td>
{{vendor.vendorAddDate}}
</td>
<td>
{{vendor.vendorUpdated|timesince}} ago
</td>
<td>
{% if vendor.vendorIsActive == 0 %}
@ -79,8 +58,7 @@
{% endif %}
</td>
<td class="text-end">
<a href="{{vendor.vendorUUID}}" class="btn btn-sm btn-neutral">Edit</a>
<a href="addresses/{{vendor.vendorUUID}}" class="btn btn-sm btn-neutral">Addresses</a>
<a href="{% url 'edit-vendor' vendor.vendorUUID %}" class="btn d-inline-flex btn-sm btn-neutral text-neutral"><span><i class="bi bi-gear me-1"></i></span> <span class="d-none d-sm-block me-2">Manage</span> </a>
</td>
</tr>
@ -90,7 +68,7 @@
</table>
</div>
<div class="card-footer border-0 py-5">
<span class="text-muted text-sm">#</span>
<div class="text-center"> <a href="{% url 'create-vendor' %}" class="btn btn-sm btn-primary">Create</a> </div>
</div>
</div>
</div>
@ -99,13 +77,5 @@
</div>
<!-- Dashboard -->
<!-- End Page-content -->
<div>
{% block footer %}
{% include "partials/footer.html" %}
{% endblock footer %}
</div>
<!-- end main content-->
{% endblock content %}

View File

@ -0,0 +1,184 @@
{% extends "partials/base.html" %}
{% load static %}
{% block title %}Vendor Edit Page{% endblock title %}
{% block content %}
<!-- ============================================================== -->
<!-- Start right Content here -->
<!-- ============================================================== -->
<!-- Main content -->
<div class="flex-fill overflow-y-lg-auto scrollbar bg-surface-primary rounded-top-4 rounded-top-start-lg-4 rounded-top-end-lg-0 border-top border-lg shadow">
<main class="container-fluid px-3 py-5 p-lg-6 p-xxl-10">
<header class="border-bottom mb-10">
<div class="row align-items-center">
<div class="col-sm-6 col-12">
<h1 class="h2 ls-tight">
{% block vendor_title %} {{vendor_title}} {% endblock vendor_title %}
</h1>
</div>
</div>
<ul class="nav nav-tabs overflow-x border-0 mt-4">
<li class="nav-item">
<a href="{% url 'edit-vendor' vendors.vendorUUID %}" class="nav-link">Options</a>
</li>
<li class="nav-item">
<a href="{% url 'add-address' vendors.vendorUUID %}" class="nav-link">Addresses</a>
</li>
<li class="nav-item">
<a href="/pages/account-billing.html" class="nav-link">Delete</a>
</li>
</ul>
</header>
<form method="POST" action="">
{% csrf_token %}
{{ form.non_field_errors }}
<div class="row align-items-center">
<div class="col-md-2">
<label for="{{ form.vendorWebName.id_for_label}}" class="form-label">Website Name <span><i class="bi bi-patch-question-fill text-danger" title="Shown on invoice page"></i></span></label>
</div>
<div class="col-md-8 col-xl-5">
<div class="">
<div class="input-group position-relative">
<span class="input-group-text"><i class="bi bi-three-dots"></i></span>
<input id="vendorWebName" name="vendorWebName" type="text" class="form-control" {% if not vendors.vendorWebName %} placeholder="Website Name" {% else %} value="{{vendors.vendorWebName}}" {% endif %} aria-label="vendorWebName">
</div>
<span class="mt-2 valid-feedback">Looks good!</span>
</div>
</div>
</div>
<p class="my-6" />
<div class="row align-items-center">
<div class="col-md-2">
<label for="{{ form.vendorWebAddr.id_for_label}}" class="form-label">Website URL <span><i class="bi bi-patch-question-fill text-danger" title="Shown on invoice page"></i></span></label>
</div>
<div class="col-md-8 col-xl-5">
<div class="">
<div class="input-group position-relative">
<span class="input-group-text"><i class="bi bi-window"></i></span>
<input id="vendorWebAddr" name="vendorWebAddr" type="text" class="form-control" {% if not vendors.vendorWebAddr %} placeholder="Website URL" {% else %} value="{{vendors.vendorWebAddr}}" {% endif %} aria-label="vendorWebAddr">
</div>
{% if form.vendorWebAddr.errors %}
{% for error in form.vendorWebAddr.errors %}
<span class="mt-2 valid-feedback">{{ error|escape }}</span>
{% endfor %}
{% endif %}
</div>
</div>
</div>
<p class="my-6" />
<div class="row align-items-center">
<div class="col-md-2">
<label for="{{ form.vendorNetworkFee.id_for_label}}" class="form-label">Network Fee % <span><i class="bi bi-patch-question-fill text-danger" title="Max percentage "></i></span> </label>
</div>
<div class="col-md-8 col-xl-2">
<select id="vendorNetworkFee" name="vendorNetworkFee" class="form-select" aria-describedby="vendorNetworkFeeHelpBlock">
<option value="0" {% if vendors.vendorNetworkFee == 0 %} selected="selected" {% endif %}>0%</option>
<option value="5" {% if vendors.vendorNetworkFee == 5 %} selected="selected" {% endif %}>5%</option>
<option value="10" {% if vendors.vendorNetworkFee == 10 %} selected="selected" {% endif %}>10%</option>
</select>
{% if form.vendorNetworkFee.errors %}
{% for error in form.vendorNetworkFee.errors %}
<span id="vendorNetworkFeeHelpBlock" class="text-danger">{{ error|escape }}</span>
{% endfor %}
{% endif %}
</div>
</div>
<p class="my-6" />
<div class="row align-items-center">
<div class="col-md-2">
<label for="vendorPaidNotification" class="form-label">Notifications <span><i class="bi bi-patch-question-fill text-danger" title="Get notified by email for each payment received?"></i></span></label>
</div>
<div class="col-md-8 col-xl-2">
<select id="vendorPaidNotification" name="vendorPaidNotification" class="form-select" aria-describedby="vendorPaidNotificationHelpBlock">
<option value="true" {% if vendors.vendorPaidNotification == True %} selected="selected" {% endif %}>Yes</option>
<option value="false" {% if vendors.vendorPaidNotification == False %} selected="selected" {% endif %}>No</option>
</select>
</div>
{% if form.vendorPaidNotification.errors %}
{% for error in form.vendorPaidNotification.errors %}
<span id="vendorPaidNotificationHelpBlock" class="text-danger">{{ error|escape }}</span>
{% endfor %}
{% endif %}
</div>
<p class="my-6" />
<div class="row align-items-center">
<div class="col-md-2">
<label for="vendorCoverAmount" class="form-label">Cover Amount <span><i class="bi bi-patch-question-fill text-danger" title="Amount covered by you in case the buyer sends less than required"></i></span> </label>
</div>
<div class="col-md-8 col-xl-2">
<select id="vendorCoverAmount" name="vendorCoverAmount" class="form-select" aria-describedby="vendorCoverAmountHelpBlock">
<option value="0" {% if vendors.vendorCoverAmount == 0 %} selected="selected" {% endif %}>0%</option>
<option value="1" {% if vendors.vendorCoverAmount == 1 %} selected="selected" {% endif %}>1%</option>
<option value="2" {% if vendors.vendorCoverAmount == 2 %} selected="selected" {% endif %}>2%</option>
<option value="3" {% if vendors.vendorCoverAmount == 3 %} selected="selected" {% endif %}>3%</option>
<option value="4" {% if vendors.vendorCoverAmount == 4 %} selected="selected" {% endif %}>4%</option>
<option value="5" {% if vendors.vendorCoverAmount == 5 %} selected="selected" {% endif %}>5%</option>
<option value="6" {% if vendors.vendorCoverAmount == 6 %} selected="selected" {% endif %}>6%</option>
<option value="7" {% if vendors.vendorCoverAmount == 7 %} selected="selected" {% endif %}>7%</option>
<option value="8" {% if vendors.vendorCoverAmount == 8 %} selected="selected" {% endif %}>8%</option>
<option value="9" {% if vendors.vendorCoverAmount == 9 %} selected="selected" {% endif %}>9%</option>
<option value="10" {% if vendors.vendorCoverAmount == 10 %} selected="selected" {% endif %}>10%</option>
</select>
{% if form.vendorCoverAmount.errors %}
{% for error in form.vendorCoverAmount.errors %}
<span id="vendorCoverAmountHelpBlock" class="text-danger">{{ error|escape }}</span>
{% endfor %}
{% endif %}
</div>
</div>
<p class="my-6" />
<div class="form-group row">
<div class="col-md-2">
<label for="vendorPayWindow" class="form-label">Payment <span><i class="bi bi-patch-question-fill text-danger" title="When should we send you your payments?"></i></span></label>
</div>
<div class="col-md-8 col-xl-2">
<select id="vendorPayWindow" name="vendorPayWindow" class="form-select" aria-describedby="vendorPayWindowHelpBlock">
<option value="true" {% if vendors.vendorPayWindow == 1 %} selected="selected" {% endif %}>Hourly</option>
<option value="false" {% if vendors.vendorPayWindow == 0 %} selected="selected" {% endif %}>Daily</option>
</select>
{% if form.vendorPayWindow.errors %}
{% for error in form.vendorPayWindow.errors %}
<span id="vendorPayWindowHelpBlock" class="text-danger">{{ error|escape }}</span>
{% endfor %}
{% endif %}
</div>
</div>
<hr class="my-6" />
<div class="form-group row">
<div class="offset-4 col-8">
<input name="submit" type="submit" class="btn btn-primary" value="Submit" />
</div>
</div>
</form>
</div>
{% if messages %}
<ul class="messages text-center">
{% for message in messages %}
<p{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</p>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
</main>
</div>
</div>
<!-- Dashboard -->
<!-- End Page-content -->
<!-- end main content-->
{% endblock content %}

View File

@ -6,6 +6,13 @@ urlpatterns = [
path('login/', views.LoginPage, name="login"),
path('logout/', views.LogoutPage, name="logout"),
path('', views.index, name="dashboard"),
path('vendor/', views.VendorPage, name="vendor"),
path('vendor/edit/<str:vdr>/', views.VendorEditPage, name="edit-vendor"),
path('vendor/create', views.VendorCreatePage, name="create-vendor"),
path('vendor/address/<str:vdr>/', views.VendorAddrPage, name="add-address"),
#path('vendor-success', views.VendorSuccess, name="vendor-success")
]

View File

@ -1,13 +1,21 @@
from django.shortcuts import render, redirect
from django.contrib import messages
from users.models import User
from .models import VendorsData
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
from django.db.models import Q
from django_otp.decorators import otp_required
#
from users.models import User
from .models import VendorsData, CryptoCoins, VendorsAddresses
from .forms import VendorEditForm, VendorAddrAddForm
from .functions import vendor_generator, checksumCheck
import uuid, hashlib
# Create your views here.
@login_required(login_url='login')
#@otp_required()
def index(request):
context = {}
return render(request, 'base/dashboard.html', context)
@ -33,7 +41,7 @@ def LoginPage(request):
context = {}
return render(request, 'base/login.html', context)
return render(request, 'base/loogin2.html', context)
def LogoutPage(request):
logout(request)
@ -41,6 +49,136 @@ def LogoutPage(request):
@login_required(login_url='login')
def VendorPage(request):
vendors = VendorsData.objects.filter(vendorid_id=request.user.id)
vendors = VendorsData.objects.filter(Q(vendorid_id=request.user.id) & Q(vendorDeleted=False))
context = {'vendors': vendors}
return render(request, 'base/vendor.html', context)
@login_required(login_url='login')
#@otp_required(login_url='two_factor:login')
def VendorEditPage(request, vdr ):
try:
vendors = VendorsData.objects.get(Q(vendorid_id=request.user.id) & Q(vendorUUID=vdr))
form = VendorEditForm()
vendor_title = "Edit Vendor"
if request.method == 'POST':
if request.POST.get('submit') == 'Submit':
try:
vendors = VendorsData.objects.get(Q(vendorid_id=request.user.id) & Q(vendorUUID=vdr))
form = VendorEditForm(request.POST, instance=vendors)
if form.is_valid():
form.save()
messages.success(request, "Vendor information saved")
return redirect('vendor')
else:
messages.error(request, "Something wrong happened, try again.")
except Exception as e:
messages.error(request, "Something wrong happened, try again.")
context = {'vendors': vendors, 'form': form, 'vendor_title': vendor_title}
return render(request, 'base/vendorEdit.html', context)
except Exception as e:
messages.error(request, e)
return redirect('vendor')
@login_required(login_url='login')
def VendorCreatePage(request):
vendor_title = "Create Vendor"
context = {'vendor_title': vendor_title}
if request.method == 'POST':
try:
try:
vendor_accounts = len(VendorsData.objects.filter(vendorid_id=request.user.id))
if vendor_accounts > request.user.vendornr:
messages.error(request, "You have reached the maximum number of vendor accounts")
return redirect('vendor')
except: pass
form = VendorEditForm(request.POST)
if form.is_valid():
# Generate vendor
new_vendor = form.save(commit=False)
try:
vdr = vendor_generator()
vendor_check = VendorsData.objects.get(vendorUUID=vdr)
while vendor_check is vdr:
vdr = vendor_generator()
vendor_check = VendorsData.objects.get(vendorUUID=vdr)
except:
pass
secretKey = str(uuid.uuid4())
md5s = hashlib.md5(secretKey.encode()).hexdigest()
new_vendor.vendor = vdr
new_vendor.vendorSecretKey = md5s
new_vendor.vendorSkipScreen = False
new_vendor.vendorUUID = uuid.uuid4()
new_vendor.vendorid_id = request.user.id
# Save vendor
try:
new_vendor.save()
vendor_title = "Congrats!"
context = {'vendor_title': vendor_title, "vendor": new_vendor, 'secret': secretKey}
return render(request, 'base/vendorSuccess.html', context)
except Exception:
messages.error(request, "Unable to create vendor") #
else:
messages.error(request, "The information submited is incomplete") #
except Exception as e:
messages.error(request, "Something wrong happened, try again.")
return render(request, 'base/vendorCreate.html', context)
@login_required(login_url='login')
def VendorAddrPage(request,vdr):
try:
coins = CryptoCoins.objects.filter(coinIsActive=True)
try:
vendors = VendorsData.objects.get(Q(vendorid_id=request.user.id) & Q(vendorUUID=vdr))
except:
messages.error(request, "Vendor not found")
return redirect('vendor')
addresses = VendorsAddresses.objects.filter(Q(vendorUUID=vdr) & Q(vendorid_id=request.user.id))
form = VendorAddrAddForm()
vendor_title = "Add a crypto address"
if request.method == 'POST':
if request.POST.get('submit') == 'Submit':
try:
if checksumCheck(request.POST.get('coin').lower(), request.POST.get('address')) == False:
messages.error(request, "Invalid address")
context = {'vendor_title': vendor_title, 'coins': coins, 'vendors': vendors, "addresses": addresses}
return render(request, 'base/vendorAddr.html', context)
except:
# stop here, return nothing, notify us.
return redirect('vendorAddr', vdr)
##check if duplicate
try:
form = VendorAddrAddForm(request.POST)
if form.is_valid():
new_address = form.save(commit=False)
new_address.vendorUUID = vdr
new_address.addrDeleted = False
new_address.addrIsActive = True
new_address.vendor_id = vendors.id
new_address.vendorid_id = request.user.id
try:
for obj in VendorsAddresses.objects.filter(Q(coin=request.POST.get('coin')) & Q(addrIsActive=True) & Q(vendorid_id=request.user.id)):
obj.addrIsActive = False
obj.save()
new_address.save()
messages.success(request, "Vendor address saved")
#add email notification
context = {'vendor_title': vendor_title, 'coins': coins, 'vendors': vendors, "addresses": addresses}
return render(request, 'base/vendorAddr.html', context)
except Exception as e:
messages.error(request, e) #
else:
messages.error(request, "Something wrong happened, try again!")
except Exception as e:
messages.error(request, "Something wrong happened, try again!")
context = {'vendor_title': vendor_title, 'coins': coins, 'vendors': vendors, "addresses": addresses}
return render(request, 'base/vendorAddr.html', context)
except Exception as e:
messages.error(request, "Something wrong happened, try again!")
return redirect('vendor')

View File

@ -26,7 +26,7 @@ SECRET_KEY = 'django-insecure-z#_ppfgs06)e4v18t!970-&-&jkxht!tw&ms#u5n_m-fbxulwa
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
ALLOWED_HOSTS = ['10.0.70.5', '127.0.0.1']
# User substitution
# https://docs.djangoproject.com/en/1.11/topics/auth/customizing/#auth-custom-user
@ -43,6 +43,13 @@ INSTALLED_APPS = [
'django.contrib.messages',
'django.contrib.staticfiles',
'django_bcrypt',
'django_otp',
'django_otp.plugins.otp_static',
'django_otp.plugins.otp_totp',
'django_otp.plugins.otp_email', # <- if you want email capability.
'two_factor',
'two_factor.plugins.phonenumber', # <- if you want phone number capability.
'two_factor.plugins.email', # <- if you want email capability.
#
'base.apps.BaseConfig',
'users.apps.UsersConfig',
@ -54,6 +61,7 @@ MIDDLEWARE = [
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django_otp.middleware.OTPMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
@ -113,13 +121,13 @@ AUTH_PASSWORD_VALIDATORS = [
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
'''
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
]
'''
# Internationalization
# https://docs.djangoproject.com/en/4.2/topics/i18n/
@ -143,23 +151,44 @@ STATICFILES_DIRS = [
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
},
},
'loggers': {
'two_factor': {
'handlers': ['console'],
'level': 'INFO',
}
}
}
# Messages customize
MESSAGE_TAGS = {
messages.DEBUG: "alert-info",
messages.INFO: "alert-info",
messages.SUCCESS: "alert-success",
messages.WARNING: "alert-warning",
messages.ERROR: "alert-danger",
messages.DEBUG: "text-info",
messages.INFO: "text-info",
messages.SUCCESS: "text-success",
messages.WARNING: "text-warning",
messages.ERROR: "text-danger",
}
BCRYPT_ROUNDS = 11
PASSWORD_RESET_TIMEOUT = 3600 #
#LOGIN_URL = 'two_factor:login'
#OTP_LOGIN_URL = 'account:login'
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
SESSION_COOKIE_SECURE = False #to be switched to true when in prod
CSRF_COOKIE_SECURE = False #to be switched to true when in prod
SECURE_SSL_REDIRECT = False #to be switched to true when in prod
SESSION_COOKIE_AGE = 3600
TWO_FACTOR_REMEMBER_COOKIE_AGE = 3600
SERVER_EMAIL = 'info@litepay.ch'
DEFAULT_FROM_EMAIL = 'info@litepay.ch'

View File

@ -16,9 +16,15 @@ Including another URLconf
"""
from django.contrib import admin
from django.urls import path, include
from two_factor.urls import urlpatterns as tf_urls
urlpatterns = [
path('admin/', admin.site.urls),
#path('', include(tf_urls)),
path('', include('base.urls'))
]
'''
urlpatterns = [
path('admin/', admin.site.urls),
]
'''

BIN
src/.DS_Store vendored

Binary file not shown.

View File

@ -18,9 +18,7 @@
</div>
<!-- End Page-content -->
{% block footer %}
{% include "partials/footer.html" %}
{% endblock footer %}
</div>
<!-- end main content-->
{% endblock content %}

View File

@ -1,23 +1,27 @@
{% load static %}
<!DOCTYPE html>
<html lang="en">
<!doctype html>
<html lang="en" data-theme="light">
<head>
<meta charset="utf-8" />
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<meta name="color-scheme" content="dark light">
<title>{% block title %}{% endblock title %} |</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta content="Premium Multipurpose Admin & Dashboard Template" name="description" />
<meta content="Themesbrand" name="author" />
<!-- App favicon -->
<link rel="shortcut icon" href="{% static 'images/favicon.ico'%}">
{% block css %}
<!-- Bootstrap Css -->
<link href="https://unpkg.com/@webpixels/css@1.1.5/dist/index.css" id="bootstrap-style" rel="stylesheet" type="text/css" />
<!-- Icons Css -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-icons/1.4.0/font/bootstrap-icons.min.css" rel="stylesheet" type="text/css" />
<!-- Styles -->
<link rel="stylesheet" type="text/css" href="{% static '/css/main.css'%}">
<link rel="stylesheet" type="text/css" href="{% static '/css/utility.css'%}">
<!-- Bootstrap Icons -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.2/font/bootstrap-icons.css">
<!-- Fonts -->
<link rel="stylesheet" href="https://api.fontshare.com/v2/css?f=satoshi@900,700,500,300,401,400&display=swap">
{% endblock css %}
</head>
<body>
<body class="bg-surface-secondary">
<!-- Begin page -->
<div id="layout-wrapper">
{% block header %}
@ -29,8 +33,6 @@
{% block content %}
{% block pagetitle %}
{% endblock pagetitle %}
{% block footer %}
{% endblock footer %}
{% endblock content %}
</div>

View File

@ -1,10 +1,9 @@
{% block footer %}
<footer class="footer">
<div class="container-fluid">
<div class="row">
<div class="col-sm-6">
<div class="col-sm-6 text-center">
<script>document.write(new Date().getFullYear())</script> © Litepay.ch.
</div>
</div>

View File

@ -1,90 +1,257 @@
{% load static %}
{% block sidebar %}
<div class="d-flex flex-column flex-lg-row h-lg-full bg-surface-secondary">
<!-- Vertical Navbar -->
<nav class="navbar show navbar-vertical h-lg-screen navbar-expand-lg px-0 py-3 navbar-light bg-white border-bottom border-bottom-lg-0 border-end-lg" id="navbarVertical">
<div class="container-fluid">
<!-- Toggler -->
<div class="d-flex flex-column flex-lg-row h-lg-full">
<nav class="flex-none navbar navbar-vertical navbar-expand-lg navbar-light bg-transparent show h-lg-screen px-0 py-2 scrollbar" id="sidebar">
<div class="container-fluid px-3 px-md-4 px-lg-6">
<button class="navbar-toggler ms-n2" type="button" data-bs-toggle="collapse" data-bs-target="#sidebarCollapse" aria-controls="sidebarCollapse" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<!-- Brand -->
<a class="navbar-brand py-lg-2 mb-lg-5 px-lg-6 me-0" href="#">
<img src="https://preview.webpixels.io/web/img/logos/clever-primary.svg" alt="...">
<a class="navbar-brand d-inline-block py-lg-1 mb-lg-5" href="/pages/dashboard.html">
<img src="https://ik.imagekit.io/litepaych/assets/new/img/logo_litepay_2020_blue.png" class="h-8 h-md-10" alt="...">
</a>
<!-- User menu (mobile) -->
<div class="navbar-user d-lg-none">
<!-- Dropdown -->
<div class="dropdown">
<!-- Toggle -->
<a href="#" id="sidebarAvatar" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<div class="avatar-parent-child">
<img alt="Image Placeholder" src="https://images.unsplash.com/photo-1548142813-c348350df52b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=3&w=256&h=256&q=80" class="avatar avatar- rounded-circle">
<span class="avatar-child avatar-badge bg-success"></span>
<a class="d-flex align-items-center" href="#" role="button" data-bs-toggle="dropdown" aria-haspopup="false" aria-expanded="false">
<div>
<div class="avatar avatar-sm text-bg-secondary rounded-circle">
AE
</div>
</div>
<div class="d-none d-sm-block ms-3">
<span class="h6">Alexis</span>
</div>
<div class="d-none d-md-block ms-md-2">
<i class="bi bi-chevron-down text-muted text-xs"></i>
</div>
</a>
<!-- Menu -->
<div class="dropdown-menu dropdown-menu-end" aria-labelledby="sidebarAvatar">
<a href="#" class="dropdown-item">Profile</a>
<a href="#" class="dropdown-item">Settings</a>
<a href="#" class="dropdown-item">Billing</a>
<hr class="dropdown-divider">
<a href="#" class="dropdown-item">Logout</a>
<div class="dropdown-menu dropdown-menu-end">
<div class="dropdown-header">
<span class="d-block text-sm text-muted mb-1">Signed in as</span>
<span class="d-block text-heading font-semibold">Alexis Enache</span>
</div>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#">
<i class="bi bi-house me-3"></i>Home
</a>
<a class="dropdown-item" href="#">
<i class="bi bi-pencil me-3"></i>Edit profile
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#">
<i class="bi bi-gear me-3"></i>Settings
</a>
<a class="dropdown-item" href="#">
<i class="bi bi-image me-3"></i>Media
</a>
<a class="dropdown-item" href="#">
<i class="bi bi-box-arrow-up me-3"></i>Share
</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="#">
<i class="bi bi-person me-3"></i>Login
</a>
</div>
</div>
</div>
<!-- Collapse -->
<div class="collapse navbar-collapse" id="sidebarCollapse">
<!-- Navigation -->
<div class="collapse navbar-collapse overflow-x-hidden" id="sidebarCollapse">
<ul class="navbar-nav">
<li class="nav-item my-1">
<a class="nav-link d-flex align-items-center rounded-pill" href="/">
<i class="bi bi-houses-fill"></i>
<span>Dashboard</span>
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto"></span>
</a>
</li>
<li class="nav-item my-1">
<a class="nav-link d-flex align-items-center rounded-pill" href="/pages/analytics.html">
<i class="bi bi-pie-chart-fill"></i>
<span>Analytics</span>
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto"></span>
</a>
</li>
<li class="nav-item my-1">
<a class="nav-link d-flex align-items-center rounded-pill" href="/pages/wallet.html">
<i class="bi bi-wallet-fill"></i>
<span>Wallet</span>
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto"></span>
</a>
</li>
<li class="nav-item my-1">
<a class="nav-link d-flex align-items-center rounded-pill" href="{% url 'vendor' %}">
<i class="bi bi-wallet-fill"></i>
<span>Vendor</span>
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto"></span>
</a>
</li>
<li class="nav-item my-1">
<a class="nav-link d-flex align-items-center rounded-pill" href="#sidebar-exchange" data-bs-toggle="collapse" role="button" aria-expanded="false" aria-controls="sidebar-exchange">
<i class="bi bi-bar-chart-fill"></i>
<span>Exchange</span>
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto"></span>
</a>
<div class="collapse" id="sidebar-exchange">
<ul class="nav nav-sm flex-column mt-1">
<li class="nav-item">
<a class="nav-link" href="#">
<i class="bi bi-house"></i> Dashboard
<a href="/pages/exchange-watchlist.html" class="nav-link">
Watchlist
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<i class="bi bi-bar-chart"></i> Analitycs
<a href="/pages/exchange-history.html" class="nav-link">
Trade History
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<i class="bi bi-chat"></i> Messages
<span class="badge bg-soft-primary text-primary rounded-pill d-inline-flex align-items-center ms-auto">6</span>
<a href="/pages/exchange-details.html" class="nav-link">
Trade Details
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<i class="bi bi-bookmarks"></i> Collections
<a href="/pages/exchange-create-form.html" class="nav-link">
Create Asset
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'vendor' %}">
<i class="bi bi-people"></i> Vendor
<a href="/pages/exchange-staking.html" class="nav-link">
Staking
</a>
</li>
</ul>
</div>
</li>
<li class="nav-item my-1">
<a class="nav-link d-flex align-items-center rounded-pill" href="#sidebar-account" data-bs-toggle="collapse" role="button" aria-expanded="false" aria-controls="sidebar-account">
<i class="bi bi-gear-fill"></i>
<span>Account</span>
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto"></span>
</a>
<div class="collapse" id="sidebar-account">
<ul class="nav nav-sm flex-column mt-1">
<li class="nav-item">
<a href="/pages/account-general.html" class="nav-link">
Settings
</a>
</li>
<li class="nav-item">
<a href="/pages/account-password.html" class="nav-link">
Password
</a>
</li>
<li class="nav-item">
<a href="/pages/account-billing.html" class="nav-link">
Billing
</a>
</li>
<li class="nav-item">
<a href="/pages/account-notifications.html" class="nav-link">
Notifications
</a>
</li>
<li class="nav-item">
<a href="/pages/login.html" class="nav-link">
Login
</a>
</li>
<li class="nav-item">
<a href="/pages/register.html" class="nav-link">
Register
</a>
</li>
</ul>
</div>
</li>
<li class="nav-item my-1">
<a class="nav-link d-flex align-items-center rounded-pill" href="#sidebar-pages" data-bs-toggle="collapse" role="button" aria-expanded="false" aria-controls="sidebar-pages">
<i class="bi bi-file-break-fill"></i>
<span>Pages</span>
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto"></span>
</a>
<div class="collapse" id="sidebar-pages">
<ul class="nav nav-sm flex-column mt-1">
<li class="nav-item">
<a href="/pages/other-pricing.html" class="nav-link">
Pricing Plans
</a>
</li>
<li class="nav-item">
<a href="/pages/terms.html" class="nav-link">
Terms of Service
</a>
</li>
<li class="nav-item">
<a href="/pages/error.html" class="nav-link">
Error Page
</a>
</li>
<li class="nav-item">
<a href="/index.html" class="nav-link">
Landing Page
</a>
</li>
</ul>
</div>
</li>
</ul>
<!-- Divider -->
<hr class="navbar-divider my-5 opacity-20">
<!-- Navigation -->
<!-- Push content down
<div class="mt-auto"></div> -->
<!-- User (md) -->
<hr class="navbar-divider my-5 opacity-70">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="{% url 'login' %}">
<i class="bi bi-person-square"></i> Account
<li>
<span class="nav-link text-xs font-semibold text-uppercase text-muted ls-wide">
Resources
</span>
</li>
<li class="nav-item my-1">
<a class="nav-link d-flex align-items-center rounded-pill" href="{% url 'logout' %}">
<i class="bi bi-door-closed-fill"></i>
<span>Logout</span>
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto"></span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'logout' %}">
<i class="bi bi-box-arrow-left"></i> Logout
<li class="nav-item my-1">
<a class="nav-link d-flex align-items-center rounded-pill" href="https://themes.getbootstrap.com/product/satoshi-defi-and-crypto-exchange-theme">
<i class="bi bi-journal-album"></i>
<span>Changelog</span>
<span class="badge badge-sm bg-soft-success text-success rounded-pill ms-auto">v1.0</span>
</a>
</li>
</ul>
<div class="mt-auto"></div>
<div class="card bg-dark border-0 mt-5 mb-3">
<div class="card-body">
<div class="vstack gap-4">
<i class="bi bi-emoji-sunglasses-fill text-primary text-xl"></i>
<p class="text-sm text-white text-opacity-70">
Upgrade your account to Pro for even more examples.
</p>
<a href="/pages/pricing.html" class="btn btn-sm btn-primary w-full rounded-pill">
Upgade now<i class="bi bi-arrow-right ms-2"></i>
</a>
</div>
</div>
</div>
</div>
</div>
</nav>
<div class="flex-lg-fill overflow-x-auto vstack h-lg-screen">
<div class="d-none d-lg-flex py-4">
<div class="flex-none">
</div>
<div class=" d-lg-none d-xxl-flex align-items-center gap-4 px-4 scrollable-x">
<div class="d-flex gap-2 text-xs">
<span class="text-heading font-semibold">Cryptos:</span>
<span class="text-muted">21,713</span>
</div>
<div class="d-flex gap-2 text-xs">
<span class="text-heading font-semibold">Market Cap:</span>
<span class="text-muted">$871,322,862,585</span>
</div>
<div class="d-flex gap-2 text-xs">
<span class="text-heading font-semibold">24h Vol:</span>
<span class="text-muted">$180,639,667,232</span>
</div>
</div>
</div>
{% endblock sidebar %}

View File

@ -1,7 +1,15 @@
{% load static %}
{% block header %}
<!-- Banner -->
<a href="#" class="btn w-full btn-primary text-truncate rounded-0 py-2 border-0 position-relative" style="z-index: 1000;">
{% if messages %}
{% for message in messages %}
<a href="#"{% if message.tags %} class="btn {{ message.tags }} w-full text-truncate rounded-0 py-2 border-0 position-relative" style="z-index: 1000;" {% endif %}>{{ message }}</a>
{% endfor %}
{% endif %}
<!--
<a href="#" class="btn btn-danger w-full text-truncate rounded-0 py-2 border-0 position-relative" style="z-index: 1000;">
<strong>Strong</strong> message here→
</a>
-->
{% endblock header %}

View File

@ -38,6 +38,7 @@ class User(AbstractUser):
username = None
email = models.EmailField(_('email address'), unique=True)
percentage = models.DecimalField(decimal_places=3, default=0.010, max_digits=4)
vendornr = models.IntegerField(default=9)
objects = UserManager()