Merge pull request 'feature/vendor' (#1) from feature/vendor into main

Reviewed-on: http://172.16.100.2:3000/mike/django/pulls/1
This commit is contained in:
mike 2023-05-30 11:33:43 +02:00
commit 79ec088059
23 changed files with 1831 additions and 484 deletions

View File

@ -1,3 +1,18 @@
from django.contrib import admin from django.contrib import admin
# Register your models here. # 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 django.db import models
from users.models import User from users.models import User
from django.contrib import admin
# Create your models here. # 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): class VendorsData(models.Model):
id = models.BigAutoField(primary_key=True)
vendorid = models.ForeignKey(User, default=None, on_delete=models.CASCADE) vendorid = models.ForeignKey(User, default=None, on_delete=models.CASCADE)
vendorUUID = models.UUIDField() vendorUUID = models.UUIDField()
vendor = models.CharField(max_length=8, unique=True) vendor = models.CharField(max_length=8, unique=True)
vendorSecretKey = models.UUIDField() vendorSecretKey = models.CharField(max_length=250)
vendorCoverAmount = models.IntegerField(default=0) vendorCoverAmount = models.IntegerField(default=0)
vendorPaidNotification = models.IntegerField(default=1) vendorNetworkFee = models.IntegerField(default=0)
vendorSkipScreen = models.IntegerField(default=0) vendorPaidNotification = models.BooleanField(default=True)
vendorPayWindow = models.IntegerField(default=0) vendorSkipScreen = models.BooleanField(default=False)
vendorPayWindow = models.BooleanField(default=True)
vendorWebName = models.CharField(max_length=250) 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) vendorAddDate = models.DateTimeField(auto_now_add=True)
vendorDeleted = models.IntegerField(default=0) vendorDeleted = models.BooleanField(default=False)
vendorAddDelete = models.DateTimeField(auto_now=True) vendorUpdated = models.DateTimeField(auto_now=True)
vendorIsActive = models.IntegerField(default=1) vendorAddDelete = models.DateTimeField(auto_now_add=True)
vendorIsActive = models.BooleanField(default=True)
def __str__(self): def __str__(self):
return self.vendor return self.vendor
def __repr__(self):
return self._repr()
class Meta: class Meta:
verbose_name = "Vendor" verbose_name = "Vendor"
verbose_name_plural = "Vendors" verbose_name_plural = "Vendors"
ordering = ["-id"] 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 --> <!-- Main content -->
<div class="h-screen flex-grow-1 overflow-y-lg-auto"> <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 -->
<main class="py-6 bg-surface-secondary"> <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">
<div class="container-fluid"> <main class="container-fluid px-3 py-5 p-lg-6 p-xxl-10">
<!-- Card stats --> <div class="mb-6 mb-xl-10">
<div class="row g-6 mb-6"> <div class="row g-3 align-items-center">
<div class="col-xl-3 col-sm-6 col-12"> <div class="col">
<div class="card shadow border-0"> <h1 class="h2 ls-tight">Dashboard</h1>
<div class="card-body"> </div>
<div class="row">
<div class="col"> </div>
<span class="h6 font-semibold text-muted text-sm d-block mb-2">Earnings</span> </div>
<span class="h3 font-bold mb-0">$750.90</span>
</div> <div class="row g-3 g-xxl-6">
<div class="col-auto"> <div class="col-xxl-12">
<div class="icon icon-shape bg-tertiary text-white text-lg rounded-circle"> <div class="vstack gap-3 gap-md-6">
<i class="bi bi-credit-card"></i> <div class="row g-3">
</div> <div class="col-md col-sm-6">
</div> <div class="card bg-success bg-opacity-10 border-success border-opacity-40">
</div> <div class="card-body p-4">
<div class="mt-2 mb-0 text-sm"> <div class="d-flex align-items-center gap-2">
<span class="badge badge-pill bg-soft-success text-success me-2"> <img class="w-5 flex-none" src="{% static '/img/crypto/color/usdt.svg' %}" alt="..." />
<i class="bi bi-arrow-up me-1"></i>13% <h6>USDT</h6>
</span>
<span class="text-nowrap text-xs text-muted">Since last month</span>
</div>
</div> </div>
</div> <div class="text-xs mt-4">
</div> Total balance
<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>
<div class="col-auto">
<div class="icon icon-shape bg-primary text-white text-lg rounded-circle">
<i class="bi bi-people"></i>
</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%
</span>
<span class="text-nowrap text-xs text-muted">Since last month</span>
</div>
</div> </div>
</div> <div class="text-sm font-semibold">
</div> 75.800,00 USDT
<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>
<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>
</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%
</span>
<span class="text-nowrap text-xs text-muted">Since last month</span>
</div>
</div> </div>
</div>
</div> </div>
</div> </div>
<div class="col-xl-3 col-sm-6 col-12"> <div class="col-md col-sm-6">
<div class="card shadow border-0"> <div class="card border-primary-hover">
<div class="card-body"> <div class="card-body p-4">
<div class="row"> <div class="d-flex align-items-center gap-2">
<div class="col"> <img class="w-5 flex-none" src="{% static '/img/crypto/color/btc.svg' %}" alt="..." />
<span class="h6 font-semibold text-muted text-sm d-block mb-2">Invoices</span> <a href="/pages/exchange-details.html" class="h6 stretched-link">BTC</a>
<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>
</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%
</span>
<span class="text-nowrap text-xs text-muted">Since last month</span>
</div>
</div> </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>+13.7%</span>
</div>
</div>
</div> </div>
</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/ada.svg' %}" alt="..." />
<a href="/pages/exchange-details.html" class="h6 stretched-link">ADA</a>
</div>
<div class="text-sm font-semibold mt-3">
10.745,49 ADA
</div>
<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>-3.2%</span>
</div>
</div>
</div>
</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/eos.svg' %}" alt="..." />
<a href="/pages/exchange-details.html" class="h6 stretched-link">EOS</a>
</div>
<div class="text-sm font-semibold mt-3">
7.890,00 EOS
</div>
<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>-2.2%</span>
</div>
</div>
</div>
</div>
<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>
<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="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>
</div>
</main>
<div class="card shadow border-0 mb-7"> </div>
<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> </div>
<!-- Dashboard --> <!-- Dashboard -->
<!-- End Page-content --> <!-- End Page-content -->
<div> <div>
{% block footer %}
{% include "partials/footer.html" %}
{% endblock footer %}
</div> </div>
<!-- end main content--> <!-- end main content-->
{% endblock content %} {% endblock content %}

View File

@ -1,6 +1,6 @@
{% extends "partials/base.html" %} {% extends "partials/base.html" %}
{% load static %} {% load static %}
{% block title %}Dashboard{% endblock title %} {% block title %}Vendor Page{% endblock title %}
{% block content %} {% block content %}
<!-- ============================================================== --> <!-- ============================================================== -->
<!-- Start right Content here --> <!-- Start right Content here -->
@ -8,24 +8,7 @@
<!-- Main content --> <!-- Main content -->
<div class="h-screen flex-grow-1 overflow-y-lg-auto"> <div class="h-screen flex-grow-1 overflow-y-lg-auto">
<!-- Header --> <!-- 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 -->
<main class="py-6 bg-surface-secondary"> <main class="py-6 bg-surface-secondary">
<div class="container-fluid"> <div class="container-fluid">
@ -39,9 +22,9 @@
<thead class="thead-light"> <thead class="thead-light">
<tr> <tr>
<th scope="col">VendorID</th> <th scope="col">VendorID</th>
<th scope="col">Creation Date</th>
<th scope="col">Website Name</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 scope="col">Status</th>
<th></th> <th></th>
</tr> </tr>
@ -50,22 +33,18 @@
<tr> <tr>
<td> <td>
<img alt="..." src="https://preview.webpixels.io/web/img/other/logos/logo-1.png" class="avatar avatar-xs rounded-circle me-2"> {{vendor.vendor}}
<a class="text-heading font-semibold" href="#">
{{vendor.vendor}}
</a>
</td> </td>
<td> <td>
{{vendor.vendorAddDate}} {{vendor.vendorWebName|truncatechars:20}}
</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>
</td> </td>
<td> <td>
{{vendor.vendorAddDate}}
</td>
<td>
{{vendor.vendorUpdated|timesince}} ago
</td> </td>
<td> <td>
{% if vendor.vendorIsActive == 0 %} {% if vendor.vendorIsActive == 0 %}
@ -79,8 +58,7 @@
{% endif %} {% endif %}
</td> </td>
<td class="text-end"> <td class="text-end">
<a href="{{vendor.vendorUUID}}" class="btn btn-sm btn-neutral">Edit</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>
<a href="addresses/{{vendor.vendorUUID}}" class="btn btn-sm btn-neutral">Addresses</a>
</td> </td>
</tr> </tr>
@ -90,7 +68,7 @@
</table> </table>
</div> </div>
<div class="card-footer border-0 py-5"> <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> </div>
</div> </div>
@ -99,13 +77,5 @@
</div> </div>
<!-- Dashboard --> <!-- Dashboard -->
<!-- End Page-content --> <!-- End Page-content -->
<div>
{% block footer %}
{% include "partials/footer.html" %}
{% endblock footer %}
</div>
<!-- end main content--> <!-- end main content-->
{% endblock 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('login/', views.LoginPage, name="login"),
path('logout/', views.LogoutPage, name="logout"), path('logout/', views.LogoutPage, name="logout"),
path('', views.index, name="dashboard"), path('', views.index, name="dashboard"),
path('vendor/', views.VendorPage, name="vendor"), 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.shortcuts import render, redirect
from django.contrib import messages 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 import authenticate, login, logout
from django.contrib.auth.decorators import login_required 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. # Create your views here.
@login_required(login_url='login') @login_required(login_url='login')
#@otp_required()
def index(request): def index(request):
context = {} context = {}
return render(request, 'base/dashboard.html', context) return render(request, 'base/dashboard.html', context)
@ -33,7 +41,7 @@ def LoginPage(request):
context = {} context = {}
return render(request, 'base/login.html', context) return render(request, 'base/loogin2.html', context)
def LogoutPage(request): def LogoutPage(request):
logout(request) logout(request)
@ -41,6 +49,136 @@ def LogoutPage(request):
@login_required(login_url='login') @login_required(login_url='login')
def VendorPage(request): 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} context = {'vendors': vendors}
return render(request, 'base/vendor.html', context) 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! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True DEBUG = True
ALLOWED_HOSTS = [] ALLOWED_HOSTS = ['10.0.70.5', '127.0.0.1']
# User substitution # User substitution
# https://docs.djangoproject.com/en/1.11/topics/auth/customizing/#auth-custom-user # https://docs.djangoproject.com/en/1.11/topics/auth/customizing/#auth-custom-user
@ -43,6 +43,13 @@ INSTALLED_APPS = [
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'django_bcrypt', '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', 'base.apps.BaseConfig',
'users.apps.UsersConfig', 'users.apps.UsersConfig',
@ -54,6 +61,7 @@ MIDDLEWARE = [
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django_otp.middleware.OTPMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
] ]
@ -113,13 +121,13 @@ AUTH_PASSWORD_VALIDATORS = [
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
}, },
] ]
'''
PASSWORD_HASHERS = [ PASSWORD_HASHERS = [
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher', 'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
] ]
'''
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/4.2/topics/i18n/ # https://docs.djangoproject.com/en/4.2/topics/i18n/
@ -143,23 +151,44 @@ STATICFILES_DIRS = [
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 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 # Messages customize
MESSAGE_TAGS = { MESSAGE_TAGS = {
messages.DEBUG: "alert-info", messages.DEBUG: "text-info",
messages.INFO: "alert-info", messages.INFO: "text-info",
messages.SUCCESS: "alert-success", messages.SUCCESS: "text-success",
messages.WARNING: "alert-warning", messages.WARNING: "text-warning",
messages.ERROR: "alert-danger", messages.ERROR: "text-danger",
} }
BCRYPT_ROUNDS = 11 BCRYPT_ROUNDS = 11
PASSWORD_RESET_TIMEOUT = 3600 # PASSWORD_RESET_TIMEOUT = 3600 #
#LOGIN_URL = 'two_factor:login'
#OTP_LOGIN_URL = 'account:login'
SESSION_EXPIRE_AT_BROWSER_CLOSE = True SESSION_EXPIRE_AT_BROWSER_CLOSE = True
SESSION_COOKIE_SECURE = False #to be switched to true when in prod SESSION_COOKIE_SECURE = False #to be switched to true when in prod
CSRF_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 SECURE_SSL_REDIRECT = False #to be switched to true when in prod
SESSION_COOKIE_AGE = 3600 SESSION_COOKIE_AGE = 3600
TWO_FACTOR_REMEMBER_COOKIE_AGE = 3600
SERVER_EMAIL = 'info@litepay.ch' 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.contrib import admin
from django.urls import path, include from django.urls import path, include
from two_factor.urls import urlpatterns as tf_urls
urlpatterns = [
#path('', include(tf_urls)),
path('', include('base.urls'))
]
'''
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path('', include('base.urls'))
] ]
'''

BIN
src/.DS_Store vendored

Binary file not shown.

View File

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

View File

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

View File

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

View File

@ -1,90 +1,257 @@
{% load static %} {% load static %}
{% block sidebar %} {% block sidebar %}
<div class="d-flex flex-column flex-lg-row h-lg-full bg-surface-secondary"> <div class="d-flex flex-column flex-lg-row h-lg-full">
<!-- Vertical Navbar --> <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">
<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 px-3 px-md-4 px-lg-6">
<div class="container-fluid"> <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">
<!-- Toggler --> <span class="navbar-toggler-icon"></span>
<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"> </button>
<span class="navbar-toggler-icon"></span> <a class="navbar-brand d-inline-block py-lg-1 mb-lg-5" href="/pages/dashboard.html">
</button> <img src="https://ik.imagekit.io/litepaych/assets/new/img/logo_litepay_2020_blue.png" class="h-8 h-md-10" alt="...">
<!-- Brand --> </a>
<a class="navbar-brand py-lg-2 mb-lg-5 px-lg-6 me-0" href="#"> <div class="navbar-user d-lg-none">
<img src="https://preview.webpixels.io/web/img/logos/clever-primary.svg" alt="..."> <div class="dropdown">
</a> <a class="d-flex align-items-center" href="#" role="button" data-bs-toggle="dropdown" aria-haspopup="false" aria-expanded="false">
<!-- User menu (mobile) --> <div>
<div class="navbar-user d-lg-none"> <div class="avatar avatar-sm text-bg-secondary rounded-circle">
<!-- Dropdown --> AE
<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>
</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>
</div> </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>
<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>
<!-- Collapse --> </div>
<div class="collapse navbar-collapse" id="sidebarCollapse">
<!-- Navigation -->
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="#">
<i class="bi bi-house"></i> Dashboard
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<i class="bi bi-bar-chart"></i> Analitycs
</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>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<i class="bi bi-bookmarks"></i> Collections
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'vendor' %}">
<i class="bi bi-people"></i> Vendor
</a>
</li>
</ul>
<!-- Divider -->
<hr class="navbar-divider my-5 opacity-20">
<!-- Navigation -->
<!-- Push content down
<div class="mt-auto"></div> -->
<!-- User (md) -->
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="{% url 'login' %}">
<i class="bi bi-person-square"></i> Account
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'logout' %}">
<i class="bi bi-box-arrow-left"></i> Logout
</a>
</li>
</ul>
</div>
</div> </div>
<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 href="/pages/exchange-watchlist.html" class="nav-link">
Watchlist
</a>
</li>
<li class="nav-item">
<a href="/pages/exchange-history.html" class="nav-link">
Trade History
</a>
</li>
<li class="nav-item">
<a href="/pages/exchange-details.html" class="nav-link">
Trade Details
</a>
</li>
<li class="nav-item">
<a href="/pages/exchange-create-form.html" class="nav-link">
Create Asset
</a>
</li>
<li class="nav-item">
<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-70">
<ul class="navbar-nav">
<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 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> </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 %} {% endblock sidebar %}

View File

@ -1,7 +1,15 @@
{% load static %} {% load static %}
{% block header %} {% block header %}
<!-- Banner --> <!-- 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→ <strong>Strong</strong> message here→
</a> </a>
-->
{% endblock header %} {% endblock header %}

View File

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