You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

django-link-shortener

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

django-link-shortener - pypi Package Compare versions

Comparing version
0.1
to
0.3
+92
shortener/shortener.py
from shortener.models import UrlMap, UrlProfile
from django.conf import settings
from django.db import IntegrityError
from datetime import timedelta
from django.utils import timezone
import random
def get_random(tries=0):
length = getattr(settings, 'SHORTENER_LENGTH', 5)
length += tries
# Removed l, I, 1
dictionary = "ABCDEFGHJKLMNOPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz234567890"
return ''.join(random.choice(dictionary) for _ in range(length))
def create(user, link):
# check if user allowed to save link
try:
# use user settings
p = UrlProfile.objects.get(user=user)
enabled = p.enabled
max_urls = p.max_urls
max_concurrent = p.max_concurrent_urls
lifespan = p.default_lifespan
max_uses = p.default_max_uses
except UrlProfile.DoesNotExist:
# Use defaults from settings
enabled = getattr(settings, 'SHORTENER_ENABLED', True)
max_urls = getattr(settings, 'SHORTENER_MAX_URLS', -1)
max_concurrent = getattr(settings, 'SHORTENER_MAX_CONCURRENT', -1)
lifespan = getattr(settings, 'SHORTENER_LIFESPAN', -1)
max_uses = getattr(settings, 'SHORTENER_MAX_USES', -1)
# Ensure User is allowed to create
if not enabled:
raise PermissionError("not authorized to create shortlinks")
# Expiry date, -1 to disable
if lifespan != -1:
expiry_date = timezone.now() + timedelta(seconds=lifespan)
else:
expiry_date = timezone.make_aware(timezone.datetime.max, timezone.get_default_timezone())
# Ensure user has not met max_urls quota
if max_urls != -1:
if UrlMap.objects.filter(user=user).count() >= max_urls:
raise PermissionError("url quota exceeded")
# Ensure user has not met concurrent urls quota
if max_concurrent != -1:
if UrlMap.objects.filter(user=user, date_expired__gt=timezone.now()).count() >= max_concurrent:
raise PermissionError("concurrent quota exceeded")
# Try up to three times to generate a random number without duplicates.
# Each time increase the number of allowed characters
for tries in range(3):
try:
short = get_random(tries)
m = UrlMap(user=user, full_url=link, short_url=short, max_count=max_uses, date_expired=expiry_date)
m.save()
return m.short_url
except IntegrityError:
continue
raise KeyError("Could not generate unique shortlink")
def expand(link):
try:
url = UrlMap.objects.get(short_url__exact=link)
except UrlMap.DoesNotExist:
raise KeyError("invalid shortlink")
# ensure we are within usage counts
if url.max_count != -1:
if url.max_count <= url.usage_count:
raise PermissionError("max usages for link reached")
# ensure we are within allowed datetime
# print(timezone.now())
# print(url.date_expired)
if timezone.now() > url.date_expired:
raise PermissionError("shortlink expired")
url.usage_count += 1
url.save()
return url.full_url
+119
-12
Metadata-Version: 1.1
Name: django-link-shortener
Version: 0.1
Version: 0.3
Summary: A simple Django Link Shortener.
Home-page: https://www.example.com/
Home-page: https://github.com/ronaldgrn/django-link-shortener
Author: Petronald Green
Author-email: petronaldgreen@gmail.com
License: MIT License
Download-URL: https://github.com/ronaldgrn/django-link-shortener/archive/0.2.tar.gz
Description: =====================

@@ -13,10 +14,27 @@ django-link-shortener

django-link-shortener is a simple time and usage quantity sensitive
url shortening app
.. image:: https://travis-ci.org/ronaldgrn/django-link-shortener.svg?branch=master
:target: https://travis-ci.org/ronaldgrn/django-link-shortener
.. image:: https://img.shields.io/pypi/l/django-link-shortener.svg
:alt: PyPI - License
:target: https://pypi.org/project/django-link-shortener/
Quick start
-----------
.. image:: https://img.shields.io/pypi/v/django-link-shortener.svg
:alt: PyPI
:target: https://pypi.org/project/django-link-shortener/
1. Add "shortener" to your INSTALLED_APPS setting like this::
django-link-shortener is a simple time and usage sensitive url shortening app.
Uses A-Za-z0-9 with the exception of I, i and 1.
Requires user to be logged in for link creation.
Usage
-----
1. pip install django-link-shortener
2. Add "shortener" to your INSTALLED_APPS setting like this::
INSTALLED_APPS = [

@@ -27,12 +45,101 @@ ...

2. Include the polls URLconf in your project urls.py like this::
3. Include the polls URLconf in your project urls.py like this::
path('s/', include('shortener.urls')),
3. Run `python manage.py migrate` to create the shortener models.
4. Run `python manage.py migrate` to create the shortener models.
4. Start the development server and visit http://127.0.0.1:8000/s/
to create a test link
5. Visit http://127.0.0.1:8000/<insert_test_code_here>/ to be redirected
Testing
-------
1. Add the following to settings
```
SHORTENER_ENABLE_TEST_PATH = True
```
1. Start the development server and visit http://127.0.0.1:8000/s/test/<My-URL-HERE>
to create a test shortcode.
or
Use shortener.create(user, link) to generate a link via code. Use shortener.expand(link)
to revert
6. Visit http://127.0.0.1:8000/s/<shortcode>/ to be redirected
Configuration Options
---------------------
Place in settings.py. Each setting be overridden on a per-user basis using the admin UrlProfile section
SHORTENER_ENABLED
Default: True
Controls whether users without a shortener profile can create shortlinks.
SHORTENER_MAX_URLS
Default: -1
Controls the default maximum limit of generated urls per account.
-1 sets infinite.
SHORTENER_MAX_CONCURRENT
Default: -1
Controls the default maximum limit of *concurrent* (active) generated urls per account.
-1 sets infinite
SHORTENER_LIFESPAN
Default: -1
Sets the default lifespan of links in seconds
-1 sets infinite
SHORTENER_MAX_USES
Default: -1
Sets the default amount of times a link can be followed
-1 sets infinite
SHORTENER_LENGTH
Default: 5
Note: Omitted from UrlProfile
Sets how many digits should be used for links.
Tries up to three times to generate a unique shortcode where
Each failure will result in length temporaily being increased by 1.
SHORTENER_ENABLE_TEST_PATH
Default: False
If true, creates shortlinks for logged in users at s/test/<<url>>/
The response is the shortcode to use used at s/<<shortcode>>
Common Use Cases
----------------
goo.gl type usage (default). Unlimited concurrent links for an unlimited length of time
::
SHORTENER_ENABLED = True
SHORTENER_MAX_URLS = -1
SHORTENER_MAX_CONCURRENT = -1
SHORTENER_LIFESPAN = -1
SHORTENER_MAX_USES = -1
Internal temporary link usage (such as on nodeferret.com). 100 Temp links per minute. 1 usage per link.
::
SHORTENER_ENABLED = True
SHORTENER_MAX_URLS = -1
SHORTENER_MAX_CONCURRENT = 100 # To prevent spamming
SHORTENER_LIFESPAN = 600
SHORTENER_MAX_USES = 1
Keywords: url shortener,link shortener
Platform: UNKNOWN

@@ -39,0 +146,0 @@ Classifier: Environment :: Web Environment

+1
-1

@@ -13,3 +13,3 @@ LICENSE

shortener/models.py
shortener/shortern.py
shortener/shortener.py
shortener/tests.py

@@ -16,0 +16,0 @@ shortener/urls.py

+119
-12
Metadata-Version: 1.1
Name: django-link-shortener
Version: 0.1
Version: 0.3
Summary: A simple Django Link Shortener.
Home-page: https://www.example.com/
Home-page: https://github.com/ronaldgrn/django-link-shortener
Author: Petronald Green
Author-email: petronaldgreen@gmail.com
License: MIT License
Download-URL: https://github.com/ronaldgrn/django-link-shortener/archive/0.2.tar.gz
Description: =====================

@@ -13,10 +14,27 @@ django-link-shortener

django-link-shortener is a simple time and usage quantity sensitive
url shortening app
.. image:: https://travis-ci.org/ronaldgrn/django-link-shortener.svg?branch=master
:target: https://travis-ci.org/ronaldgrn/django-link-shortener
.. image:: https://img.shields.io/pypi/l/django-link-shortener.svg
:alt: PyPI - License
:target: https://pypi.org/project/django-link-shortener/
Quick start
-----------
.. image:: https://img.shields.io/pypi/v/django-link-shortener.svg
:alt: PyPI
:target: https://pypi.org/project/django-link-shortener/
1. Add "shortener" to your INSTALLED_APPS setting like this::
django-link-shortener is a simple time and usage sensitive url shortening app.
Uses A-Za-z0-9 with the exception of I, i and 1.
Requires user to be logged in for link creation.
Usage
-----
1. pip install django-link-shortener
2. Add "shortener" to your INSTALLED_APPS setting like this::
INSTALLED_APPS = [

@@ -27,12 +45,101 @@ ...

2. Include the polls URLconf in your project urls.py like this::
3. Include the polls URLconf in your project urls.py like this::
path('s/', include('shortener.urls')),
3. Run `python manage.py migrate` to create the shortener models.
4. Run `python manage.py migrate` to create the shortener models.
4. Start the development server and visit http://127.0.0.1:8000/s/
to create a test link
5. Visit http://127.0.0.1:8000/<insert_test_code_here>/ to be redirected
Testing
-------
1. Add the following to settings
```
SHORTENER_ENABLE_TEST_PATH = True
```
1. Start the development server and visit http://127.0.0.1:8000/s/test/<My-URL-HERE>
to create a test shortcode.
or
Use shortener.create(user, link) to generate a link via code. Use shortener.expand(link)
to revert
6. Visit http://127.0.0.1:8000/s/<shortcode>/ to be redirected
Configuration Options
---------------------
Place in settings.py. Each setting be overridden on a per-user basis using the admin UrlProfile section
SHORTENER_ENABLED
Default: True
Controls whether users without a shortener profile can create shortlinks.
SHORTENER_MAX_URLS
Default: -1
Controls the default maximum limit of generated urls per account.
-1 sets infinite.
SHORTENER_MAX_CONCURRENT
Default: -1
Controls the default maximum limit of *concurrent* (active) generated urls per account.
-1 sets infinite
SHORTENER_LIFESPAN
Default: -1
Sets the default lifespan of links in seconds
-1 sets infinite
SHORTENER_MAX_USES
Default: -1
Sets the default amount of times a link can be followed
-1 sets infinite
SHORTENER_LENGTH
Default: 5
Note: Omitted from UrlProfile
Sets how many digits should be used for links.
Tries up to three times to generate a unique shortcode where
Each failure will result in length temporaily being increased by 1.
SHORTENER_ENABLE_TEST_PATH
Default: False
If true, creates shortlinks for logged in users at s/test/<<url>>/
The response is the shortcode to use used at s/<<shortcode>>
Common Use Cases
----------------
goo.gl type usage (default). Unlimited concurrent links for an unlimited length of time
::
SHORTENER_ENABLED = True
SHORTENER_MAX_URLS = -1
SHORTENER_MAX_CONCURRENT = -1
SHORTENER_LIFESPAN = -1
SHORTENER_MAX_USES = -1
Internal temporary link usage (such as on nodeferret.com). 100 Temp links per minute. 1 usage per link.
::
SHORTENER_ENABLED = True
SHORTENER_MAX_URLS = -1
SHORTENER_MAX_CONCURRENT = 100 # To prevent spamming
SHORTENER_LIFESPAN = 600
SHORTENER_MAX_USES = 1
Keywords: url shortener,link shortener
Platform: UNKNOWN

@@ -39,0 +146,0 @@ Classifier: Environment :: Web Environment

+114
-10

@@ -5,10 +5,27 @@ =====================

django-link-shortener is a simple time and usage quantity sensitive
url shortening app
.. image:: https://travis-ci.org/ronaldgrn/django-link-shortener.svg?branch=master
:target: https://travis-ci.org/ronaldgrn/django-link-shortener
.. image:: https://img.shields.io/pypi/l/django-link-shortener.svg
:alt: PyPI - License
:target: https://pypi.org/project/django-link-shortener/
Quick start
-----------
.. image:: https://img.shields.io/pypi/v/django-link-shortener.svg
:alt: PyPI
:target: https://pypi.org/project/django-link-shortener/
1. Add "shortener" to your INSTALLED_APPS setting like this::
django-link-shortener is a simple time and usage sensitive url shortening app.
Uses A-Za-z0-9 with the exception of I, i and 1.
Requires user to be logged in for link creation.
Usage
-----
1. pip install django-link-shortener
2. Add "shortener" to your INSTALLED_APPS setting like this::
INSTALLED_APPS = [

@@ -19,11 +36,98 @@ ...

2. Include the polls URLconf in your project urls.py like this::
3. Include the polls URLconf in your project urls.py like this::
path('s/', include('shortener.urls')),
3. Run `python manage.py migrate` to create the shortener models.
4. Run `python manage.py migrate` to create the shortener models.
4. Start the development server and visit http://127.0.0.1:8000/s/
to create a test link
5. Visit http://127.0.0.1:8000/<insert_test_code_here>/ to be redirected
Testing
-------
1. Add the following to settings
```
SHORTENER_ENABLE_TEST_PATH = True
```
1. Start the development server and visit http://127.0.0.1:8000/s/test/<My-URL-HERE>
to create a test shortcode.
or
Use shortener.create(user, link) to generate a link via code. Use shortener.expand(link)
to revert
6. Visit http://127.0.0.1:8000/s/<shortcode>/ to be redirected
Configuration Options
---------------------
Place in settings.py. Each setting be overridden on a per-user basis using the admin UrlProfile section
SHORTENER_ENABLED
Default: True
Controls whether users without a shortener profile can create shortlinks.
SHORTENER_MAX_URLS
Default: -1
Controls the default maximum limit of generated urls per account.
-1 sets infinite.
SHORTENER_MAX_CONCURRENT
Default: -1
Controls the default maximum limit of *concurrent* (active) generated urls per account.
-1 sets infinite
SHORTENER_LIFESPAN
Default: -1
Sets the default lifespan of links in seconds
-1 sets infinite
SHORTENER_MAX_USES
Default: -1
Sets the default amount of times a link can be followed
-1 sets infinite
SHORTENER_LENGTH
Default: 5
Note: Omitted from UrlProfile
Sets how many digits should be used for links.
Tries up to three times to generate a unique shortcode where
Each failure will result in length temporaily being increased by 1.
SHORTENER_ENABLE_TEST_PATH
Default: False
If true, creates shortlinks for logged in users at s/test/<<url>>/
The response is the shortcode to use used at s/<<shortcode>>
Common Use Cases
----------------
goo.gl type usage (default). Unlimited concurrent links for an unlimited length of time
::
SHORTENER_ENABLED = True
SHORTENER_MAX_URLS = -1
SHORTENER_MAX_CONCURRENT = -1
SHORTENER_LIFESPAN = -1
SHORTENER_MAX_USES = -1
Internal temporary link usage (such as on nodeferret.com). 100 Temp links per minute. 1 usage per link.
::
SHORTENER_ENABLED = True
SHORTENER_MAX_URLS = -1
SHORTENER_MAX_CONCURRENT = 100 # To prevent spamming
SHORTENER_LIFESPAN = 600
SHORTENER_MAX_USES = 1

@@ -12,4 +12,4 @@ import os

name='django-link-shortener',
version='0.1',
packages=find_packages(),
version='0.3',
packages=['shortener'],
include_package_data=True,

@@ -19,5 +19,7 @@ license='MIT License',

long_description=README,
url='https://www.example.com/',
url='https://github.com/ronaldgrn/django-link-shortener',
download_url='https://github.com/ronaldgrn/django-link-shortener/archive/0.2.tar.gz',
author='Petronald Green',
author_email='petronaldgreen@gmail.com',
keywords=['url shortener', 'link shortener'],
classifiers=[

@@ -24,0 +26,0 @@ 'Environment :: Web Environment',

from django.contrib import admin
from .models import UrlMap, UrlProfile
# Register your models here.
admin.site.register(UrlMap)
admin.site.register(UrlProfile)

@@ -1,2 +0,2 @@

# Generated by Django 2.0.5 on 2018-05-31 23:29
# Generated by Django 2.0.5 on 2018-06-01 03:53

@@ -6,3 +6,2 @@ from django.conf import settings

import django.db.models.deletion
import shortener.models

@@ -29,3 +28,3 @@

('date_created', models.DateTimeField(auto_now_add=True)),
('date_expired', models.DateTimeField(default=shortener.models.get_expiry)),
('date_expired', models.DateTimeField()),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),

@@ -39,6 +38,6 @@ ],

('enabled', models.BooleanField(default=True)),
('url_count', models.IntegerField(default=0)),
('max_urls', models.IntegerField(default=-1)),
('max_concurrent_urls', models.IntegerField(default=100)),
('default_lifespan', models.IntegerField(default=120)),
('default_max_uses', models.IntegerField(default=-1)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),

@@ -45,0 +44,0 @@ ],

from django.db import models
from django.contrib.auth.models import User
from django.conf import settings
from datetime import datetime, timedelta

@@ -7,8 +7,4 @@

# Create your models here.
def get_expiry():
return datetime.now() + timedelta(seconds=120)
class UrlMap(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
full_url = models.CharField(max_length=256)

@@ -20,3 +16,3 @@ short_url = models.CharField(max_length=50, unique=True, db_index=True)

date_created = models.DateTimeField(auto_now_add=True)
date_expired = models.DateTimeField(default=get_expiry)
date_expired = models.DateTimeField()

@@ -28,5 +24,4 @@ def __str__(self):

class UrlProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
enabled = models.BooleanField(default=True)
url_count = models.IntegerField(default=0)
max_urls = models.IntegerField(default=-1)

@@ -37,1 +32,2 @@ max_concurrent_urls = models.IntegerField(default=100)

default_lifespan = models.IntegerField(default=120)
default_max_uses = models.IntegerField(default=-1)
from django.test import TestCase
from shortener import shortener
from tests.testapp.models import CustomUser
import time
# Create your tests here.
class UrlMapTestCase(TestCase):
def setUp(self):
self.bob = CustomUser.objects.create_user('bob', 'bob@bob.com', 'bobpassword')
self.alice = CustomUser.objects.create_user('alice', 'alice@alice.com', 'alicepassword')
def test_url_creation(self):
url = shortener.create(self.bob, "http://devget.net/")
self.assertEqual(shortener.expand(url), "http://devget.net/")
def test_invalid_link(self):
url = shortener.create(self.bob, "http://devget.net/")
self.assertEqual(shortener.expand(url), "http://devget.net/") # good shortlink
with self.assertRaisesMessage(KeyError, 'invalid shortlink'):
self.assertEqual(shortener.expand('photosynthesis'), "http://devget.net/") # bad shortlink
def test_shortener_enabled_setting(self):
with self.settings(SHORTENER_ENABLED=False):
with self.assertRaisesMessage(PermissionError, 'not authorized to create shortlinks'):
url = shortener.create(self.bob, "http://devget.net/")
def test_max_urls_setting(self):
with self.settings(SHORTENER_MAX_URLS=2):
shortener.create(self.bob, "http://devget.net/")
shortener.create(self.bob, "http://devget.net/")
with self.assertRaisesMessage(PermissionError, 'url quota exceeded'):
shortener.create(self.bob, "http://devget.net/")
def test_max_concurrent_setting(self):
with self.settings(SHORTENER_MAX_CONCURRENT=2):
shortener.create(self.bob, "http://devget.net/")
shortener.create(self.bob, "http://devget.net/")
with self.assertRaisesMessage(PermissionError, 'concurrent quota exceeded'):
shortener.create(self.bob, "http://devget.net/")
def test_lifespan_setting(self):
with self.settings(SHORTENER_LIFESPAN=4): # 4 seconds
url = shortener.create(self.bob, "http://blog.devget.net/")
self.assertEqual(shortener.expand(url), "http://blog.devget.net/")
time.sleep(5)
with self.assertRaisesMessage(PermissionError, 'shortlink expired'):
self.assertEqual(shortener.expand(url), "http://blog.devget.net/")
def test_max_uses_setting(self):
with self.settings(SHORTENER_MAX_USES=2):
url = shortener.create(self.bob, "http://blog.devget.net/")
self.assertEqual(shortener.expand(url), "http://blog.devget.net/")
self.assertEqual(shortener.expand(url), "http://blog.devget.net/")
with self.assertRaisesMessage(PermissionError, 'max usages for link reached'):
# Ensure error is raised when we hit limit
shortener.expand(url)
from django.urls import path
from shortener import views
from django.conf import settings
urlpatterns = [
path('', views.index),
path('<link>/', views.expand),
]
if getattr(settings, 'SHORTENER_ENABLE_TEST_PATH', False):
urlpatterns.append(path('test/<path:link>', views.test))
from django.shortcuts import redirect
from django.http import HttpResponse
from shortener import shortern
from shortener import shortener
# Create your views here.
def index(request):
data = shortern.convert(request.user, "http://google.com/")
return HttpResponse(data)
def test(request, link):
if request.user.is_authenticated:
data = shortener.create(request.user, link)
return HttpResponse(data)
else:
return HttpResponse('unauthorized')
def expand(request, link):
link = shortern.revert(link)
if link:
# return HttpResponse(shortern.revert(link))
return redirect(str(link.full_url)) # TODO: permanent=True
else:
HttpResponse("Something bad happened")
try:
link = shortener.expand(link)
return redirect(link) # TODO: permanent=True
except Exception as e:
return HttpResponse(e.args)
from shortener.models import UrlMap, UrlProfile
from django.conf import settings
from django.db import IntegrityError
import random
import string
def get_random(tries=0):
if hasattr(settings, 'SHORTENER_LENGTH'):
length = settings.SHORTENER_LENGTH
else:
length = 5
length += tries
# Removed l, I, 1
# return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(length))
return ''.join(random.choice("ABCDEFGHJKLMNOPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz234567890") for _ in range(length))
def convert(user, link):
# Try up to three times to generate a random number without duplicates.
# Each time increase the number of allowed characters
for tries in range(3):
try:
short = get_random(tries)
m = UrlMap(user=user, full_url=link, short_url=short)
m.save()
return m.short_url
except IntegrityError:
continue
raise KeyError("Could not generate unique shortlink")
def revert(link):
try:
url = UrlMap.objects.get(short_url__exact=link)
return url
except UrlMap.DoesNotExist:
return -1 # TODO: improve this