{"id":13512294,"url":"https://github.com/amitt001/pygmy","last_synced_at":"2025-04-12T21:19:55.073Z","repository":{"id":26929889,"uuid":"111005273","full_name":"amitt001/pygmy","owner":"amitt001","description":"An open-source, feature rich \u0026 extensible url-shortener + analytics written in Python :cookie:","archived":false,"fork":false,"pushed_at":"2024-03-07T17:11:02.000Z","size":1828,"stargazers_count":711,"open_issues_count":18,"forks_count":136,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-04-04T00:11:27.648Z","etag":null,"topics":["analytics","django","flask","pygmy","python","python3","shortener","url-shortener","urlshortener"],"latest_commit_sha":null,"homepage":"https://demo.pygy.co/pygmy","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/amitt001.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-11-16T18:06:14.000Z","updated_at":"2025-03-03T09:25:46.000Z","dependencies_parsed_at":"2024-11-01T14:31:13.008Z","dependency_job_id":"0929920b-11c6-4942-be6d-2c78169ac091","html_url":"https://github.com/amitt001/pygmy","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amitt001%2Fpygmy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amitt001%2Fpygmy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amitt001%2Fpygmy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/amitt001%2Fpygmy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/amitt001","download_url":"https://codeload.github.com/amitt001/pygmy/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248632142,"owners_count":21136630,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["analytics","django","flask","pygmy","python","python3","shortener","url-shortener","urlshortener"],"created_at":"2024-08-01T03:01:41.946Z","updated_at":"2025-04-12T21:19:55.043Z","avatar_url":"https://github.com/amitt001.png","language":"Python","funding_links":[],"categories":["Python","Self Hosting Opensource","后端 \u003ca name=\"backend\"\u003e\u003c/a\u003e"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\u003cimg src=\"pygmyui/static/logo/logov2.png\" alt=\"pygmy\" height=\"200px\"\u003e\u003c/p\u003e\n\n\u003cdiv align=\"center\"\u003e\n  \u003ch1\u003ePygmy\u003c/h1\u003e\n\n\u003c!-- [![Build Status](https://travis-ci.org/amitt001/pygmy.svg?branch=master)](https://travis-ci.org/amitt001/pygmy) --\u003e\n\n[![Coverage Status](https://img.shields.io/coveralls/github/amitt001/pygmy.svg?color=yellowgreen)](https://coveralls.io/github/amitt001/pygmy?branch=master)\n![PyPI - Python Version](https://img.shields.io/pypi/pyversions/Django.svg)\n[![PyPI license](https://img.shields.io/pypi/l/ansicolortags.svg)](https://github.com/amitt001/pygmy/blob/master/LICENSE)\n![Docker Pulls](https://img.shields.io/docker/pulls/amit19/pygmy.svg)\n\nDemo Version: [https://demo.pygy.co](https://demo.pygy.co)\n\nLink stats(add **+** to the URL) example: [demo.pygy.co/pygmy+](https://demo.pygy.co/pygmy+)\n\nHackernews Thread: https://news.ycombinator.com/item?id=17690559\n\u003c/div\u003e\n\n# Table of Contents\n- [Table of Contents](#table-of-contents)\n- [Features](#features)\n- [Technical Info](#technical-info)\n- [Setup](#setup)\n  - [Docker](#docker)\n  - [Manual(from source)](#manualfrom-source)\n  - [DB Setup:](#db-setup)\n    - [Use MySQL](#use-mysql)\n    - [Use Postgresql](#use-postgresql)\n    - [Use SQLite](#use-sqlite)\n  - [Docker](#docker-1)\n  - [Using Pygmy API](#using-pygmy-api)\n    - [Create User](#create-user)\n  - [Shell Usage](#shell-usage)\n- [Development](#development)\n  - [Run tests:](#run-tests)\n      - [Run tests with coverage report](#run-tests-with-coverage-report)\n- [Sponsorship](#sponsorship)\n- [License](#license)\n\nPygmy or `pygy.co` is an open-source, extensible \u0026 easy-to-use but powerful URL shortener. It's created keeping in mind that it should be easy to host and run your custom URL shortener without much effort. [Open-source Python URL shortener]\n\nThe architecture is very loosely coupled which allows custom integrations easily.\n\n**The project has 3 major parts**\n\n- The core URL shortening code\n- A REST API on top. Uses Flask framework\n- The UI layer for rendering the UI. It uses the Django framework\n\n# Features\n\n- URL shortener\n- Customized short URL's(ex: `pygy.co/pygmy`)\n- Support to create auto expiry URL after some time.\n- Secret key protected URL's\n- User Login/Sign up to track shortened URL's and link stats\n- User dashboard\n- Link Analytics(add + to the tiny URL to get link stats)\n\n# Technical Info\n\n- Python 3, Javascript, JQuery, HTML, CSS\n- REST API: Flask\n- Pygmyui: Django(It serves the web user-interface)\n- Supported DBs: PostgreSQL/MySQL/SQLite\n- Others: SQLAlchmey, JWT\n- Docker\n- Docker-compose\n\n# Setup\n\n## Docker\n\n1. In terminal run this command: `docker pull amit19/pygmy`\n2. Then run the container: `docker run -it -p 8000:8000 amit19/pygmy`\n3. Open http://localhost:8000 in your browser\n\n## Manual(from source)\n\n1. Clone `git clone https://github.com/amitt001/pygmy.git \u0026 cd pygmy`\n2. (Optional) Install virtualenv (optional but recommended)\n    - `virtualenv -p python3 env`\n    - `source env/bin/activate`\n3. Install dependencies: `pip3 install -r requirements.txt` (if you are using MySQL or PostgreSQL check [DB setup](#db-setup) section)\n4. `python run.py` (It runs Flask and Django servers using gunicorn)\n5. Visit `127.0.0.1:8000` to use the app\n6. Logs can be viewed at `pygmy/data/pygmy.log`\n\nNote:\n\n - **This module only supports Python 3. Make sure pip and virtualenv are both python 3 based versions.**(To install Python 3 on Mac: http://docs.python-guide.org/en/latest/starting/install3/osx/)\n - The project has two config files:\n    - pygmy.cfg: `pygmy/config/pygmy.cfg` rest API and pygmy core settings file\n    - settings.py: `pygmyui/pygmyui/settings.py` Django settings file\n - SQLite is default DB, if you are using PostgreSQL or MySQL with this project, make sure they are installed into the system.\n - You can run pygmy shell also. Present in the root directory. To run the program on the terminal: `python shell`\n - By default, DEBUG is set to True in `pygmyui/pygmyui/settings.py` file, set it to False in production.\n\n## DB Setup:\n\nBy default, Pygmy uses SQLite but any of the DB, SQLite, MySQL or PostgreSQL, can be used. Configs is present at `pygmy/config/pygmy.cfg`.\n\nUse DB specific instruction below. Make sure to check and modify values in pygmy.cfg file according to your DB setup.\n\n### Use MySQL\n\n1. Install pymysql: `pip install pymysql`\n\n2. Check correct port: `mysqladmin variables | grep port`\n\n3. Change below line in `pygmy/core/pygmy.cfg` file:\n\n```\n[database]\nengine: mysql\nurl: {engine}://{user}:{password}@{host}:{port}/{db_name}\nuser: root\npassword: root\nhost: 127.0.0.1\nport: 3306\ndb_name: pygmy\n```\n\n4. Enter MySQL URL `CREATE DATABASE pygmy;`\n\nNote: It's better to use Mysql with version \u003e `5.6.5` to use the default value of `CURRENT_TIMESTAMP` for `DATETIME`.\n\n### Use Postgresql\n\n1. Change below line in `pygmy/core/pygmy.cfg` file:\n\n```\n[database]\nengine: postgresql\nurl: {engine}://{user}:{password}@{host}:{port}/{db_name}\nuser: root\npassword: root\nhost: 127.0.0.1\nport: 5432\ndb_name: pygmy\n```\n\n### Use SQLite\n\n\u003e SQLite is natively supported in Python\n\n1. Update `sqlite:////var/lib/pygmy/pygmy.db` file\n\n```\n[database]\nengine: sqlite3\nsqlite_data_dir: data\nsqlite_db_file_name: pygmy.db\n```\n\n## Docker\n\nDocker image name: `amit19/pygmy`. Docker image can be built by running `docker build -t amit19/pygmy .` command. Both the Dockerfile and docker-compose file are present at the root of the project. To use docker-compose you need to pass DB credentials in the docker-compose file.\n\n## Using Pygmy API\n\n### Create User\n\n    curl -XPOST http://127.0.0.1:9119/api/user/1 -H 'Content-Type: application/json' -d '{\n    \"email\": \"amit@gmail.com\",\n    \"f_name\": \"Amit\",\n    \"l_name\": \"Tripathi\",\n    \"password\": \"a_safe_one\"\n    }'\n\n## Shell Usage\n\nOpen shell using ./shell. Available context in shell are: pygmy, Config, DB, etc. See all context by using pygmy_context.\n\nShorten a link:\n\n```\nIn [1]: shorten('http://iamit.xyz')\nOut[1]:\n{'created_at': '15 Nov, 2017 17:33:42',\n 'description': None,\n 'expire_after': None,\n 'hits_counter': 0,\n 'id': 'http://0.0.0.0:9119/api/link/5',\n 'is_custom': False,\n 'is_disabled': False,\n 'is_protected': False,\n 'long_url': 'http://iamit.xyz',\n 'owner': None,\n 'secret_key': '',\n 'short_code': 'f',\n 'short_url': 'http://pygy.co/f',\n 'updated_at': '2017-11-15T17:33:42.772520+00:00'}\n\nIn [2]: shorten('http://iamit.xyz', request=1)\nOut[2]: \u003cpygmy.model.link.Link at 0x105ca1b70\u003e\n\nIn [3]: unshorten('f')\nOut[3]:\n{'created_at': '15 Nov, 2017 17:33:42',\n 'description': None,\n 'expire_after': None,\n 'hits_counter': 0,\n 'id': 'http://0.0.0.0:9119/api/link/5',\n 'is_custom': False,\n 'is_disabled': False,\n 'is_protected': False,\n 'long_url': 'http://iamit.xyz',\n 'owner': None,\n 'secret_key': '',\n 'short_code': 'f',\n 'short_url': 'http://pygy.co/f',\n 'updated_at': '2017-11-15T17:33:42.772520+00:00'}\n\nIn [4]: link_stats('f')\nOut[4]:\n{'country_stats': 0,\n 'created_at': datetime.datetime(2017, 11, 15, 17, 33, 42, 772520),\n 'long_url': 'http://iamit.xyz',\n 'referrer': 0,\n 'short_code': 'f',\n 'time_series_base': None,\n 'time_stats': 0,\n 'total_hits': 0}\n\nIn [5]: # check the available context of the shell\nIn [6]: pygmy_context\n\nIn [7]: # Create custom short URL\n\nIn [8]: shorten('http://iamit.xyz', short_code='amit')\nOut[8]:\n{'long_url': 'http://iamit.xyz',\n 'short_code': 'amit',\n 'short_url': 'http://pygy.co/amit'}\n\nIn [9]: shorten?\nSignature: shorten(long_url, short_code=None, expire_after=None, description=None, secret_key=None, owner=None, request=None)\nDocstring:\n    Helper class that has been delegated the task of inserting the\n    passed url in DB, base 62 encoding from DB id and return the short\n    URL value.\n```\n\nQ. How Link Stats Are Generated?\n\u003e For getting geo location stats from IP maxminds' [GeoLite2-Country.mmd](http://demo.pygy.co/cm) database is used. It's in `pygmy/app` directory.\n\nQ. How Pygmy Auth Token Works?\n\u003e It uses JWT. When user logs in using username and password two tokens are generated, refresh token and auth token. Auth token is used for authentication with the Pygmy API. The refresh token can only be used to generate a new auth token. Auth token has a very short TTL but refresh token has a longer TTL. After 30 minutes. When a request comes with the old auth token and a new token is generated from the refresh token API. User passwords are encrypted by [bcrypt](https://en.wikipedia.org/wiki/Bcrypt) hash algorithm.\n\n# Development\n\nIf you find any bug, have a question or a general feature request. Open an issue on the 'Issue' page.\n\nTo contribute to the project:\n\n1. Clone the repo and make changes\n2. Build the code: `docker build pygmy`\n3. Test the changer by running: `docker run -it -p 8000:8000 pygmy`\n4. The website will be available at http://127.0.0.1:8000/\n\n## Run tests:\n\n1. Install pytest (if not already installed): `pip install pytest`\n2. In root directory run command: `py.test`\n\n#### Run tests with coverage report\n\n1. Install coverage `pip install coverage`\n2. Run command: `coverage run --omit=\"*/templates*,*/venv*,*/tests*\" -m py.test`\n3. See coverage report(Coverage numbers are low as the coverage for integration tests is not generated): `coverage report`\n\n# Sponsorship\nThe demo version of this website is made possible due to the generous sponsorship of DigitalOcean\n\n\u003cimg src=\"https://github.com/amitt001/pygmy/assets/7390944/e8df3143-d9a5-4582-8e73-6f1c1822d046\" width=\"200\" /\u003e\n\n# License\n\nMIT License\n\nCopyright (c) 2022 Amit Tripathi(https://twitter.com/amitt019)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n[Read License Terms](https://github.com/amitt001/pygmy/blob/master/LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famitt001%2Fpygmy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Famitt001%2Fpygmy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Famitt001%2Fpygmy/lists"}