/LLS

An open source URL shortener written in GO

Primary LanguageGoGNU Affero General Public License v3.0AGPL-3.0

Lls

Lls is a link shortener with built-in statistics functionality that allows for convenient link shortening. It is written in GO and uses the gin-gonic framework, as well as mangodb as its database, It is open-source and can run on most platforms.

It provides convenient APIs that enable users to quickly access the link shortening functions of the application. Additionally, the application uses request Captcha and request throttling to avoid malicious use and improve the security and stability of the system.

The application also features two management interfaces, which allow users to query statistics and delete previously created links, and has password protection. Furthermore, the application is built using Docker technology, allowing for quick start-up and improving portability and scalability.

Lls is an efficient, secure, and user-friendly application that can be used to shorten links and track link usage.

Run with docker

This was built using Docker-Compose. In order to run in your machine, just clone the repository and copy your foreground project to . /resources/ui/ and run:

  • cp ./resources/statik/mongo-init.js ./resources/statik/app.ini ./
  • perl -p -i -e "s/VFSNnSFLvfOwFnBh/{Set_database_password_here}/g" ./mongo-init.js ./app.ini
  • sudo docker-compose up --build -d (first time, then you go up without the --build, which is much faster)

If you don't have docker installed in your computer, please download and install it at: https://www.docker.com/

File structure description:

├── /app.ini
|   ├── Server configuration file (Created automatically on first boot).
|
├── /logs/
|   ├── Directory to store server log files, including access logs and error logs.
|
├── /db-data/
|   ├── Directory to store MongoDB database data. This folder is used to persist MongoDB data in Docker containers.
|
├── /resources/
|   ├── /ui/
|   |   ├── Directory to store static files for the frontend, such as HTML, CSS, and JavaScript files.
|   ├── /statik/
|   |   ├── Directory to store resource files that the server needs.
|
└── /statik/
    └── /statik.go
        ├── File that stores the Go code generated by Statik after packaging. This file contains all files under the /resources/ directory.

File and folder description:

/app.ini

This file is the server configuration file, which contains various parameters and settings required for the server to run.

/logs/

This folder is used to store server log files, including access logs and error logs.

/db-data/

This folder is used to persist MongoDB data in Docker containers. In this folder, you can persist MongoDB database data so that it can continue to be used after the container is restarted.

/resources/ui/

This folder stores static files for the frontend, such as HTML, CSS, and JavaScript files.

/resources/statik/

This folder stores resource files that the server needs, such as images and fonts.

/statik/statik.go

This file is a Go code file generated automatically by the Statik tool, which is used to embed all files under the /resources/ directory into Go code so that they can be accessed as static files.

Configuration File

This file provides various settings for the LLS Server.

Please be aware that LLS will read the app.ini file located in the working directory as its configuration file. If this file is not present, the program will automatically generate it upon startup.

General Settings:

  • GENERATE_SEED: Seed for generating random values.
  • ALLOW_ALL_PROTOCOL: Allow Shortening of non-HTTP protocols.

Logging Settings:

  • DEBUG: Toggle to print debug logs (true or false).

Internationalization Settings:

  • ADD_EXTRA_LANGUAGE: Add extra languages (true or false).
  • EXTRA_LANGUAGE_NAME: Additional language using BCP 47 Code.
  • EXTRA_LANGUAGE_FILES: Path to the language resource file.

HTTP Server Settings:

  • LISTEN: Server's listening address.
  • BASE_PATH: Base URL path for LLS.
  • SOFT_REDIRECT_BASE_PATH: URL path for Soft Redirects.
  • RANDOM_SESSION_SECRET: Generate a random session secret (true or false).
  • SESSION_SECRET: Set session secret (used if RANDOM_SESSION_SECRET is false).
  • DISABLE_STATIC_FILES_DIR_EMBED: Disable embedded static files (true or false).
  • STATIC_FILES_DIR_URI: Directory for external static files (used if DISABLE_STATIC_FILES_DIR_EMBED is true).
  • LOOSE_CORS: A lenient CORS (Cross-Origin Resource Sharing) configuration implies relaxed security policies, allowing code from any origin to access the server.

HTTP Rate Limiter Settings:

  • ENABLE_LIMITER: Enable the rate limiter (true or false).
  • LIMIT_RATE: Max requests per second.
  • LIMIT_BURST: Max concurrent requests.
  • TIMEOUT: Request timeout (in milliseconds).

DB Settings:

  • TYPE: Type of database (BadgerDB or MongoDB).

BadgerDB Settings (used if DB.TYPE is BadgerDB):

  • WITH_IN_MEMORY: Use memory mode for BadgerDB (true or false).
  • PATH: BadgerDB storage location (used if WITH_IN_MEMORY is false).

MongoDB Database Settings (used if DB.TYPE is MongoDB):

  • CLUSTER: Use MongoDB in cluster mode (true or false).
  • IP: MongoDB server address.
  • IPS: Replica set IPs (used if CLUSTER is true).
  • PORT: Server's port.
  • USER: Server's user.
  • PASSWORD: Server's password (keep confidential).
  • DATABASE: Name of the connected database.
  • CONNECT_TIMEOUT: Connection timeout (in seconds).
  • EXECUTE_TIMEOUT: Execution timeout (in seconds).
  • MIN_POOL_SIZE: Minimum size for the connection pool.
  • MAX_POOL_SIZE: Maximum size for the connection pool.
  • MAX_CONN_IDLE_TIME: Connection idle timeout (in minutes).

API Instructions

Captcha

To perform a create/manage operation you need to create Captcha first, just http GET to {BasePath}/api/captcha, The API will return the following:

{
  "code":0,
  "data":{
    "pic":"data:image/png;base64,....."
  },
  "detail":"",
  "fail":false,
  "message":"",
  "success":true,
  "type":""
}

Then, a Base64-encoded challenge image and a cookie identifying the Session are returned

Generate

To shorten a URL, just http POST to {BasePath}/api/generate_link with the following json payload (example):

{
  "link":"http://127.0.0.1:8040/", //Original URL
  "captcha":"8", //Captcha answer
  "pwd": "", //Shortened Access Password
  "expire": 1696982400, //Link Expire Time (Second Timestamp)
  "memo": "memo" //Link Memo
}

The api will return the following:

{
  "code":0,
  "data":{
    "hash":"18nfqL", //shortened URL Hash
    "token":"IKmXKMrVtBOvdibt" //Manage Password
  },
  "detail":"",
  "fail":false,
  "message":"",
  "success":true,
  "type":""
}

The token is your subsequent credentials for managing the link, and the hash is the shortened URL Hash

Redirect

just HTTP GET request to {BasePath}/s/:hash. This action will lead you to the original URL that was shortened. Here's an example:

{BasePath}/s/18nfqL?...Parameters

The redirection supports the following parameters:

Parameter Description
pwd This is the password required for accessing the shortened URL. If an incorrect or empty password is provided, you will be redirected to {SoftRedirectBasePath}/#/PasswordRedirect/:hash
soft This parameter indicates whether a soft redirect is required. If used, you will be redirected to {SoftRedirectBasePath}/#/SoftRedirect/:hash
detect This parameter activates the Detect mode. Instead of a redirection, data will be returned in JSON format

If an incorrect or empty password is provided for a password-protected shortened URL, you will be redirected to {SoftRedirectBasePath}/#/PasswordRedirect/:hash. The front-end will handle the subsequent logic.

If the soft parameter is used, you will be redirected to {SoftRedirectBasePath}/#/SoftRedirect/:hash. The front-end will handle the subsequent logic.

If the URL is a non-HTTP protocol, soft redirect will be performed.

If the detect parameter is used, the API will return the following JSON data:

{
  "code":0,
  "data":{
    "hash":"18nfqL", //shortened URL Hash
    "url":"http://127.0.0.1:8040/", //Original URL
    "expire": 1696982400, //Link Expire Time (Second Timestamp)
    "memo": "memo" //Link Memo
  },
  "detail":"",
  "fail":false,
  "message":"",
  "success":true,
  "type":""
}

In this mode, no redirection will occur. Instead, the data will be returned in JSON format.

Statistics

The application will record the visit and write it to the database, just http POST to {BasePath}/api/stats_link with the following json payload (example):

{
  "hash": "18nfqL", //shortened URL Hash
  "token": "IKmXKMrVtBOvdibt", //Manage Password
  "captcha": "25" //Captcha answer
  "page": 1, // Page number of current visit(A positive integer)
  "size": 50 //Size per page(integers from 1-100)
}

The api will return the following:

{
  "code":0,
  "data":{
    "current":1, //current page
    "size":50, //Size set by request
    "pages":1, //Total number of pages
    "total":1, //Total number of results
    "records":[
      {
        "Hash":"18nfqL", //HASH of the query
        "IP":"127.0.0.1", //IP of the visitor
        "Header":{ //Request Header of the visitor
          "Accept-Encoding":[
            "gzip, deflate, br"
          ]
        },
        "Country":"Local Address", //The country indicated by the visitor's IP
        "Area":"Local Address", //The area indicated by the visitor's IP
        "Browser":"Chrome", //The Browser indicated by the visitor's UA
        "BrowserVersion":"109.0.0",//The Browser Version indicated by the visitor's UA
        "OS":"Windows", //The OS indicated by the visitor's UA
        "OSVersion":"10", //The OS Version indicated by the visitor's UA
        "Device":"Other", //The Device indicated by the visitor's UA
        "Created":1675143659 //Access time (seconds timestamp)
      }
    ]
  },
  "detail":"",
  "fail":false,
  "message":"",
  "success":true,
  "type":""
}

It will show detailed data about the URL accessed.

Delete

If the link needs to be removed, just http POST to {BasePath}/api/delete_link with the following json payload (example):

{
  "hash": "18nfqL", //shortened URL Hash
  "token": "IKmXKMrVtBOvdibt", //Manage Password
  "captcha": "32" //Captcha answer
}

The api will return the following:

{
  "code":0,
  "data":null,
  "detail":"",
  "fail":false,
  "message":"",
  "success":true,
  "type":""
}

The link will be marked for deletion, but note that it can still be queried for statistics using the administrative password.