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.
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 ./static/resources/mongo-init.js ./static/resources/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/
├── /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.
|
├── /static/
| ├── /ui/
| | ├── Directory to store static files for the frontend, such as HTML, CSS, and JavaScript files.
| ├── /resources/
| | ├── Directory to store resource files that the server needs.
|
└── /fs/
└── /statik.go
├── File that stores the Go code generated by Statik after packaging. This file contains all files under the /resources/ directory.
This file is the server configuration file, which contains various parameters and settings required for the server to run.
This folder is used to store server log files, including access logs and error logs.
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.
This folder stores static files for the frontend, such as HTML, CSS, and JavaScript files.
This folder stores resource files that the server needs, such as images and fonts.
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.
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.
GENERATE_SEED
: Seed for generating random values.ALLOW_ALL_PROTOCOL
: Allow Shortening of non-HTTP protocols.
DEBUG
: Toggle to print debug logs (true
orfalse
).
LANGUAGE
: GeoIP2 language using BCP 47 Code.USE_ONLINE_GEOIP2
: Whether to use the online GeoIP2 (true
orfalse
).
ADD_EXTRA_LANGUAGE
: Add extra languages (true
orfalse
).EXTRA_LANGUAGE_NAME
: Additional language using BCP 47 Code.EXTRA_LANGUAGE_FILES
: Path to the language resource file.
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
orfalse
).SESSION_SECRET
: Set session secret (used ifRANDOM_SESSION_SECRET
isfalse
).DISABLE_STATIC_FILES_DIR_EMBED
: Disable embedded static files (true
orfalse
).STATIC_FILES_DIR_URI
: Directory for external static files (used ifDISABLE_STATIC_FILES_DIR_EMBED
istrue
).LOOSE_CORS
: A lenient CORS (Cross-Origin Resource Sharing) configuration implies relaxed security policies, allowing code from any origin to access the server.
ENABLE_LIMITER
: Enable the rate limiter (true
orfalse
).LIMIT_RATE
: Max requests per second.LIMIT_BURST
: Max concurrent requests.TIMEOUT
: Request timeout (in milliseconds).
TYPE
: Type of database (BadgerDB
orMongoDB
).
WITH_IN_MEMORY
: Use memory mode for BadgerDB (true
orfalse
).PATH
: BadgerDB storage location (used ifWITH_IN_MEMORY
isfalse
).
CLUSTER
: Use MongoDB in cluster mode (true
orfalse
).IP
: MongoDB server address.IPS
: Replica set IPs (used ifCLUSTER
istrue
).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).
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
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
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.
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.
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.