|
|
|
from django.core.exceptions import ValidationError
|
|
|
|
from django.db import models
|
|
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
from xenua.django.models import RandomSlugPKMixin
|
|
|
|
|
|
|
|
|
|
|
|
class Domain(models.Model):
|
|
|
|
fqdn = models.CharField(
|
|
|
|
_('FQDN'),
|
|
|
|
max_length=512,
|
|
|
|
unique=True,
|
|
|
|
)
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return self.fqdn
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get_from_request(cls, r):
|
|
|
|
return cls.objects.get(fqdn__iexact=r.get_host())
|
|
|
|
|
|
|
|
|
|
|
|
def validate_location(loc):
|
|
|
|
if "/" in loc:
|
|
|
|
raise ValidationError(
|
|
|
|
_('location must not contain a slash')
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
class ShortLink(RandomSlugPKMixin, models.Model):
|
|
|
|
class Meta:
|
|
|
|
unique_together = ('domain', 'location')
|
|
|
|
|
|
|
|
domain = models.ForeignKey(Domain, related_name='links', on_delete=models.CASCADE)
|
|
|
|
location = models.CharField(_("short link"), max_length=100, validators=[validate_location])
|
|
|
|
to = models.URLField(_("redirect to"), max_length=2500)
|
|
|
|
click_count = models.IntegerField(default=0)
|
|
|
|
|
|
|
|
cache = {}
|
|
|
|
|
|
|
|
def click(self): # todo: further assess performance. initial testing suggests minimal impact
|
|
|
|
self.click_count += 1
|
|
|
|
self.save()
|
|
|
|
|
|
|
|
def link(self):
|
|
|
|
return f"{self.domain.fqdn}/{self.location}"
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def get_from_request(cls, req, loc):
|
|
|
|
d = Domain.get_from_request(req)
|
|
|
|
return cls.objects.filter(domain=d).get(location=loc)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def hit(cls, req, loc):
|
|
|
|
if r := cls.try_cache(req, loc):
|
|
|
|
return r
|
|
|
|
|
|
|
|
return cls.miss(req, loc)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def miss(cls, req, loc):
|
|
|
|
lnk = cls.get_from_request(req, loc)
|
|
|
|
cls.cache[req.get_host()][loc] = lnk.to
|
|
|
|
return lnk.to
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def try_cache(cls, req, loc):
|
|
|
|
host = req.get_host()
|
|
|
|
try:
|
|
|
|
domaincache = cls.cache[host]
|
|
|
|
try:
|
|
|
|
return domaincache[loc]
|
|
|
|
except KeyError:
|
|
|
|
cls.miss(req, loc)
|
|
|
|
except KeyError:
|
|
|
|
cls.cache[host] = {}
|
|
|
|
|
|
|
|
return ''
|
|
|
|
|