You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

79 lines
2.0 KiB

2 years ago
from django.core.exceptions import ValidationError
2 years ago
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())
2 years ago
def validate_location(loc):
if "/" in loc:
raise ValidationError(
_('location must not contain a slash')
)
2 years ago
class ShortLink(RandomSlugPKMixin, models.Model):
class Meta:
unique_together = ('domain', 'location')
domain = models.ForeignKey(Domain, related_name='links', on_delete=models.CASCADE)
2 years ago
location = models.CharField(_("short link"), max_length=100, validators=[validate_location])
2 years ago
to = models.URLField(_("redirect to"), max_length=2500)
click_count = models.IntegerField(default=0)
cache = {}
2 years ago
def click(self): # todo: further assess performance. initial testing suggests minimal impact
2 years ago
self.click_count += 1
self.save()
def link(self):
return f"{self.domain.fqdn}/{self.location}"
@classmethod
2 years ago
def get_from_request(cls, req, loc):
d = Domain.get_from_request(req)
2 years ago
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):
2 years ago
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 ''