A PHP-based self-hosted URL shortener that can be used to serve shortened URLs under your own custom domain.

Overview

Shlink

Build Status Code Coverage Latest Stable Version Docker pulls License Paypal donate

A PHP-based self-hosted URL shortener that can be used to serve shortened URLs under your own custom domain.

Table of Contents

Full documentation

This document contains the very basics to get started with Shlink. If you want to learn everything you can do with it, visit the full searchable documentation.

Docker image

Starting with version 1.15.0, an official docker image is provided. You can learn how to use it by reading the docs.

The idea is that you can just generate a container using the image and provide the custom config via env vars.

Self hosted

First, make sure the host where you are going to run shlink fulfills these requirements:

  • PHP 8.0
  • The next PHP extensions: json, curl, pdo, intl, gd and gmp.
    • apcu extension is recommended if you don't plan to use swoole.
    • xml extension is required if you want to generate QR codes in svg format.
  • MySQL, MariaDB, PostgreSQL, Microsoft SQL Server or SQLite.
  • The web server of your choice with PHP integration (Apache or Nginx recommended).

Download

In order to run Shlink, you will need a built version of the project. There are two ways to get it.

  • Using a dist file

    The easiest way to install shlink is by using one of the pre-bundled distributable packages.

    Go to the latest version and download the shlink*_dist.zip file that suits your needs. You will find one for every supported PHP version and with/without swoole integration.

    Finally, decompress the file in the location of your choice.

  • Building from sources

    If for any reason you want to build the project yourself, follow these steps:

    • Clone the project with git (git clone https://github.com/shlinkio/shlink.git), or download it by clicking the Clone or download green button.
    • Download the Composer PHP package manager inside the project folder.
    • Run ./build.sh 1.0.0, replacing the version with the version number you are going to build (the version number is used as part of the generated dist file name, and to set the value returned when running shlink -V from the command line).

    After that, you will have a dist file inside the build directory, that you need to decompress in the location of your choice.

    This is the process used when releasing new shlink versions. After tagging the new version with git, the Github release is automatically created by a GitHub workflow, attaching the generated dist file to it.

Configure

Despite how you built the project, you now need to configure it, by following these steps:

  • If you are going to use MySQL, MariaDB, PostgreSQL or Microsoft SQL Server, create an empty database with the name of your choice.
  • Recursively grant write permissions to the data directory. Shlink uses it to cache some information.
  • Setup the application by running the bin/install script. It is a command line tool that will guide you through the installation process. Take into account that this tool has to be run directly on the server where you plan to host Shlink. Do not run it before uploading/moving it there.
  • Generate your first API key by running bin/cli api-key:generate. You will need the key in order to interact with shlink's API.

Using shlink

Once shlink is installed, there are two main ways to interact with it:

  • The command line. Try running bin/cli and see all the available commands.

    All of those commands can be run with the --help/-h flag in order to see how to use them and all the available options.

    It is probably a good idea to symlink the CLI entry point (bin/cli) to somewhere in your path, so that you can run shlink from any directory.

  • The REST API. The complete docs on how to use the API can be found here, and a sandbox which also documents every endpoint can be found in the API Spec portal.

    However, you probably don't want to consume the raw API yourself. That's why a nice web client is provided that can be directly used from https://app.shlink.io, or hosted by yourself.

Both the API and CLI allow you to do the same operations, except for API key management, which can be done from the command line interface only.

Contributing

If you are trying to find out how to run the project in development mode or how to provide contributions, read the CONTRIBUTING doc.


This product includes GeoLite2 data created by MaxMind, available from https://www.maxmind.com

Comments
  • worker_id must be less than task_worker_num

    worker_id must be less than task_worker_num

    How Shlink is set-up

    • Shlink Version: 2.3.0
    • PHP Version: docker image one
    • How do you serve Shlink: Docker image (stable image on Raspberry Pi, arm/v7)
    • Database engine used: SQLite

    Summary

    When accessing to a generated short link, I get a 500 error.

    Current behavior

    When accessing to a generated link, I get a 500 error and the log shows the following exception

    [2020-10-03T10:43:40.941875+00:00] [NULL] Shlink.ERROR - ErrorException: Swoole\Server::task(): worker_id must be less than task_worker_num[16] in /etc/shlink/vendor/shlinkio/shlink-event-dispatcher/src/Listener/AsyncEventListener.php:22
    Stack trace:
    #0 [internal function]: Laminas\Stratigility\Middleware\ErrorHandler->Laminas\Stratigility\Middleware\{closure}(2, 'Swoole\\Server::...', '/etc/shlink/ven...', 22, Array)
    #1 /etc/shlink/vendor/shlinkio/shlink-event-dispatcher/src/Listener/AsyncEventListener.php(22): Swoole\Server->task(Object(Shlinkio\Shlink\EventDispatcher\Listener\EventListenerTask))
    #2 /etc/shlink/vendor/phly/phly-event-dispatcher/src/EventDispatcher.php(39): Shlinkio\Shlink\EventDispatcher\Listener\AsyncEventListener->__invoke(Object(Shlinkio\Shlink\Core\EventDispatcher\ShortUrlVisited))
    #3 /etc/shlink/module/Core/src/Service/VisitsTracker.php(48): Phly\EventDispatcher\EventDispatcher->dispatch(Object(Shlinkio\Shlink\Core\EventDispatcher\ShortUrlVisited))
    #4 /etc/shlink/module/Core/src/Action/AbstractTrackingAction.php(58): Shlinkio\Shlink\Core\Service\VisitsTracker->track(Object(Shlinkio\Shlink\Core\Entity\ShortUrl), Object(Shlinkio\Shlink\Core\Model\Visitor))
    #5 /etc/shlink/vendor/mezzio/mezzio/src/Middleware/LazyLoadingMiddleware.php(47): Shlinkio\Shlink\Core\Action\AbstractTrackingAction->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))
    #6 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Mezzio\Middleware\LazyLoadingMiddleware->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))
    #7 /etc/shlink/vendor/akrabat/ip-address-middleware/src/IpAddress.php(91): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest))
    #8 /etc/shlink/vendor/mezzio/mezzio/src/Middleware/LazyLoadingMiddleware.php(47): RKA\Middleware\IpAddress->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))
    #9 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Mezzio\Middleware\LazyLoadingMiddleware->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))
    #10 /etc/shlink/vendor/laminas/laminas-stratigility/src/MiddlewarePipe.php(84): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest))
    #11 /etc/shlink/vendor/mezzio/mezzio-router/src/Route.php(101): Laminas\Stratigility\MiddlewarePipe->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))
    #12 /etc/shlink/vendor/mezzio/mezzio-router/src/RouteResult.php(111): Mezzio\Router\Route->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))
    #13 /etc/shlink/vendor/mezzio/mezzio-router/src/Middleware/DispatchMiddleware.php(36): Mezzio\Router\RouteResult->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))
    #14 /etc/shlink/vendor/mezzio/mezzio/src/Middleware/LazyLoadingMiddleware.php(47): Mezzio\Router\Middleware\DispatchMiddleware->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))
    #15 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Mezzio\Middleware\LazyLoadingMiddleware->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))
    #16 /etc/shlink/vendor/laminas/laminas-stratigility/src/MiddlewarePipe.php(84): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest))
    #17 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Laminas\Stratigility\MiddlewarePipe->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))
    #18 /etc/shlink/vendor/laminas/laminas-stratigility/src/Middleware/PathMiddlewareDecorator.php(48): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest))
    #19 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Laminas\Stratigility\Middleware\PathMiddlewareDecorator->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))
    #20 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(54): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest))
    #21 /etc/shlink/vendor/mezzio/mezzio-router/src/Middleware/ImplicitHeadMiddleware.php(85): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest))
    #22 /etc/shlink/vendor/mezzio/mezzio/src/Middleware/LazyLoadingMiddleware.php(47): Mezzio\Router\Middleware\ImplicitHeadMiddleware->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))
    #23 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Mezzio\Middleware\LazyLoadingMiddleware->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))
    #24 /etc/shlink/vendor/mezzio/mezzio-router/src/Middleware/RouteMiddleware.php(57): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest))
    #25 /etc/shlink/vendor/mezzio/mezzio/src/Middleware/LazyLoadingMiddleware.php(47): Mezzio\Router\Middleware\RouteMiddleware->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))
    #26 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Mezzio\Middleware\LazyLoadingMiddleware->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))
    #27 /etc/shlink/vendor/laminas/laminas-stratigility/src/MiddlewarePipe.php(84): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest))
    #28 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Laminas\Stratigility\MiddlewarePipe->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))
    #29 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(54): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest))
    #30 /etc/shlink/vendor/shlinkio/shlink-common/src/Middleware/CloseDbConnectionMiddleware.php(27): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest))
    #31 /etc/shlink/vendor/mezzio/mezzio/src/Middleware/LazyLoadingMiddleware.php(47): Shlinkio\Shlink\Common\Middleware\CloseDbConnectionMiddleware->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))
    #32 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Mezzio\Middleware\LazyLoadingMiddleware->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))
    #33 /etc/shlink/vendor/laminas/laminas-stratigility/src/MiddlewarePipe.php(84): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest))
    #34 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Laminas\Stratigility\MiddlewarePipe->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))
    #35 /etc/shlink/vendor/laminas/laminas-stratigility/src/Middleware/PathMiddlewareDecorator.php(48): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest))
    #36 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Laminas\Stratigility\Middleware\PathMiddlewareDecorator->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))
    #37 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(54): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest))
    #38 /etc/shlink/vendor/laminas/laminas-stratigility/src/Middleware/ErrorHandler.php(144): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest))
    #39 /etc/shlink/vendor/mezzio/mezzio/src/Middleware/LazyLoadingMiddleware.php(47): Laminas\Stratigility\Middleware\ErrorHandler->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))
    #40 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Mezzio\Middleware\LazyLoadingMiddleware->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))
    #41 /etc/shlink/vendor/mezzio/mezzio-helpers/src/ContentLengthMiddleware.php(32): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest))
    #42 /etc/shlink/vendor/mezzio/mezzio/src/Middleware/LazyLoadingMiddleware.php(47): Mezzio\Helper\ContentLengthMiddleware->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))
    #43 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Mezzio\Middleware\LazyLoadingMiddleware->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))
    #44 /etc/shlink/vendor/laminas/laminas-stratigility/src/MiddlewarePipe.php(84): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest))
    #45 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Laminas\Stratigility\MiddlewarePipe->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))
    #46 /etc/shlink/vendor/laminas/laminas-stratigility/src/MiddlewarePipe.php(84): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest))
    #47 /etc/shlink/vendor/laminas/laminas-stratigility/src/MiddlewarePipe.php(73): Laminas\Stratigility\MiddlewarePipe->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\EmptyPipelineHandler))
    #48 /etc/shlink/vendor/mezzio/mezzio-swoole/src/SwooleRequestHandlerRunner.php(244): Laminas\Stratigility\MiddlewarePipe->handle(Object(Laminas\Diactoros\ServerRequest))
    #49 {main}
    [2020-10-03T10:43:40.947553+00:00] [NULL] Access.ERROR - 192.168.0.8 - - "GET /shlink/FSDpx HTTP/1.1" 500 875
    
    

    Expected behavior

    No exception

    How to reproduce

    Here is the docker-compose file I use and start with docker-compose up -d. It's using traefik for the routing but I don't think it's linked.

    networks:
      shlink-network:
        name: shlink-network
      traefik-network:
        name: traefik-network
    services:
      shlink:
        container_name: shlink
        depends_on:
        - traefik
        environment:
          BASE_PATH: /shlink
          GEOLITE_LICENSE_KEY: MGinbtU64pzfFdUt
          SHORT_DOMAIN_HOST: ${DOMAIN}
          SHORT_DOMAIN_SCHEMA: https
        image: shlinkio/shlink:stable
        labels:
          traefik.docker.network: shlink-network
          traefik.enable: 'true'
          traefik.http.routers.shlink.entrypoints: web
          traefik.http.routers.shlink.rule: PathPrefix(`/shlink/`) || Path(`/shlink`)
          traefik.http.routers.shlink.service: shlink
          traefik.http.services.shlink.loadbalancer.server.port: 8080
        networks:
          shlink-network: {}
        restart: unless-stopped
      traefik:
        container_name: traefik
        environment:
        - TRAEFIK_API=true
        - TRAEFIK_DASHBOARD=true
        - TRAEFIK_PROVIDERS_DOCKER=true
        - TRAEFIK_PROVIDERS_DOCKER_ENDPOINT=unix:///var/run/docker.sock
        - TRAEFIK_PROVIDERS_DOCKER_EXPOSEDBYDEFAULT=false
        - TRAEFIK_LOG_LEVEL=DEBUG
        - TRAEFIK_GLOBAL_SENDANONYMOUSUSAGE=false
        - TRAEFIK_ENTRYPOINTS_WEB_ADDRESS=:80
        - TRAEFIK_ENTRYPOINTS_WEBSECURE_ADDRESS=:443
        image: traefik:latest
        labels:
          traefik.enable: 'true'
          traefik.http.middlewares.dashboard-addslash.redirectregex.regex: ^(.*)/dashboard$$
          traefik.http.middlewares.dashboard-addslash.redirectregex.replacement: $${1}/dashboard/
          traefik.http.routers.api.entrypoints: web
          traefik.http.routers.api.middlewares: dashboard-addslash@docker
          traefik.http.routers.api.rule: PathPrefix(`/api/`) || PathPrefix(`/dashboard/`) || Path(`/api`) || Path(`/dashboard`)
          traefik.http.routers.api.service: api@internal
        networks:
          shlink-network: {}
          traefik-network: {}
        ports:
        - 80:80
        - 443:443
        restart: unless-stopped
        volumes:
        - /var/run/docker.sock:/var/run/docker.sock:ro
    
    

    Then I create a link with e.g.:

    $ docker exec -it shlink shlink short-url:generate https://google.com      
    Processed long URL: https://google.com
    Generated short URL: https://<mydomain>/shlink/zSPry
    

    And when I access the short link, I get the error (whatever the target)

    bug 
    opened by leolivier 31
  • Updating working docker shlink to 2.9.1 and fails to start.

    Updating working docker shlink to 2.9.1 and fails to start.

    How Shlink is set-up

    • Shlink Version: 2.9.1
    • PHP Version: n/a
    • How do you serve Shlink: Docker Image on Ubuntu
    • Database engine used: MariaDB

    Summary

    Run update of the docker container, reloaded it, and the server fails to start..

    Current behavior

    The server does not start.

    Expected behavior

    Expected the server to start. It ran well on version 2.9.0

    How to reproduce

    Update a working docker container from 2.9.0 to 2.9.1.

    Additional information from logs (seems to imply database access issues):

    Shlink.ERROR - Shlinkio\Shlink\Rest\Exception\MercureException: This Shlink instance is not integrated with a mercure hub. in /etc/shlink/module/Rest/src/Exception/MercureException.php:21 Shlink.ERROR - PDOException: SQLSTATE[42S22]: Column not found: 1054 Unknown column 's0_.forward_query' in 'field list' in /etc/shlink/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php:137 #0 /etc/shlink/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php(137): PDO->query('SELECT DISTINCT...', NULL) #1 /etc/shlink/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOQueryImplementation.php(24): Doctrine\DBAL\Driver\PDOConnection->doQuery('SELECT DISTINCT...', NULL)

    Next Doctrine\DBAL\Exception\InvalidFieldNameException: An exception occurred while executing 'SELECT DISTINCT s0_.id AS id_0, s0_.original_url AS original_url_1, s0_.short_code AS short_code_2, s0_.date_created AS date_created_3, s0_.valid_since AS valid_since_4, s0_.valid_until AS valid_until_5, s0_.max_visits AS max_visits_6, s0_.import_source AS import_source_7, s0_.import_original_short_code AS import_original_short_code_8, s0_.title AS title_9, s0_.title_was_auto_resolved AS title_was_auto_resolved_10, s0_.crawlable AS crawlable_11, s0_.forward_query AS forward_query_12, s0_.domain_id AS domain_id_13, s0_.author_api_key_id AS author_api_key_id_14 FROM short_urls s0_ WHERE 1 = 1 ORDER BY s0_.date_created DESC LIMIT 5':

    Also, shlink-web-client works after a docker image update (but cannot connect to the server).

    Also, reverting to the previous shlink:290 image works fine and the server runs.

    bug 
    opened by cwldev 28
  • MSET error with AWS Redis on ElasticCache implementation

    MSET error with AWS Redis on ElasticCache implementation

    How Shlink is set-up

    • Shlink Version: 2.7.0
    • PHP Version: 8.0.6
    • How do you serve Shlink: Self-hosted Docker image
    • Database engine used: MySQL 5.7.22

    Summary

    Recently Shlink started reporting intermitence on our self-hosted service. We are using AWS ECS to deploy using official shlink docker image, mysql on RDS and Redis on ElasticCache.

    We are using an API endpoint in order to generate short links, but its not wokring as expected. According to logs the only visible error is:

    Shlink.ERROR - Predis\NotSupportedException: Cannot use 'MSET' over clusters of connections. in /etc/shlink/vendor/predis/predis/src/Connection/Aggregate/PredisCluster.php:134

    Seems to be a problem with predis implementation.

    Stack trace is below. Any help is greatly appreciated

    1621877034521,[2021-05-24T17:23:54.520840+00:00] [18ec5525-6c25-4d84-a080-329638785be8] Shlink.ERROR - Predis\NotSupportedException: Cannot use 'MSET' over clusters of connections. in /etc/shlink/vendor/predis/predis/src/Connection/Aggregate/PredisCluster.php:134 1621877034521,Stack trace: 1621877034521,#0 /etc/shlink/vendor/predis/predis/src/Connection/Aggregate/PredisCluster.php(215): Predis\Connection\Aggregate\PredisCluster->getConnection(Object(Predis\Command\StringSetMultiple)) 1621877034521,#1 /etc/shlink/vendor/predis/predis/src/Client.php(331): Predis\Connection\Aggregate\PredisCluster->executeCommand(Object(Predis\Command\StringSetMultiple)) 1621877034521,#2 /etc/shlink/vendor/predis/predis/src/Client.php(314): Predis\Client->executeCommand(Object(Predis\Command\StringSetMultiple)) 1621877034521,"#3 /etc/shlink/vendor/doctrine/cache/lib/Doctrine/Common/Cache/PredisCache.php(78): Predis\Client->__call('mset', Array)" 1621877034521,"#4 /etc/shlink/vendor/doctrine/cache/lib/Doctrine/Common/Cache/CacheProvider.php(99): Doctrine\Common\Cache\PredisCache->doSaveMultiple(Array, 0)" 1621877034521,"#5 /etc/shlink/vendor/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/CacheAdapter.php(305): Doctrine\Common\Cache\CacheProvider->saveMultiple(Array, 0)" 1621877034521,"#6 /etc/shlink/vendor/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/CacheAdapter.php(215): Doctrine\Common\Cache\Psr6\CacheAdapter->doSaveMultiple(Array, 0)" 1621877034521,#7 /etc/shlink/vendor/doctrine/cache/lib/Doctrine/Common/Cache/Psr6/CacheAdapter.php(91): Doctrine\Common\Cache\Psr6\CacheAdapter->commit() 1621877034521,#8 /etc/shlink/vendor/doctrine/persistence/lib/Doctrine/Persistence/Mapping/AbstractClassMetadataFactory.php(258): Doctrine\Common\Cache\Psr6\CacheAdapter->getItems(Array) 1621877034521,#9 /etc/shlink/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php(300): Doctrine\Persistence\Mapping\AbstractClassMetadataFactory->getMetadataFor('Shlinkio\Shlink...') 1621877034521,#10 /etc/shlink/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php(1290): Doctrine\ORM\EntityManager->getClassMetadata('Shlinkio\Shlink...') 1621877034521,#11 /etc/shlink/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php(396): Doctrine\ORM\UnitOfWork->getCommitOrder() 1621877034521,#12 /etc/shlink/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php(383): Doctrine\ORM\UnitOfWork->commit(NULL) 1621877034521,#13 /etc/shlink/vendor/doctrine/orm/lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php(181): Doctrine\ORM\EntityManager->flush(NULL) 1621877034521,#14 /etc/shlink/module/Core/src/Visit/VisitsTracker.php(81): Doctrine\ORM\Decorator\EntityManagerDecorator->flush() 1621877034521,#15 /etc/shlink/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php(254): Shlinkio\Shlink\Core\Visit\VisitsTracker->Shlinkio\Shlink\Core\Visit{closure}(Object(Doctrine\ORM\EntityManager)) 1621877034521,#16 /etc/shlink/vendor/doctrine/orm/lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php(69): Doctrine\ORM\EntityManager->transactional(Object(Closure)) 1621877034521,#17 /etc/shlink/module/Core/src/Visit/VisitsTracker.php(84): Doctrine\ORM\Decorator\EntityManagerDecorator->transactional(Object(Closure)) 1621877034521,"#18 /etc/shlink/module/Core/src/Visit/VisitsTracker.php(34): Shlinkio\Shlink\Core\Visit\VisitsTracker->trackVisit(Object(Closure), Object(Shlinkio\Shlink\Core\Model\Visitor))" 1621877034521,"#19 /etc/shlink/module/Core/src/Action/AbstractTrackingAction.php(57): Shlinkio\Shlink\Core\Visit\VisitsTracker->track(Object(Shlinkio\Shlink\Core\Entity\ShortUrl), Object(Shlinkio\Shlink\Core\Model\Visitor))" 1621877034521,"#20 /etc/shlink/vendor/mezzio/mezzio/src/Middleware/LazyLoadingMiddleware.php(41): Shlinkio\Shlink\Core\Action\AbstractTrackingAction->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))" 1621877034521,"#21 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Mezzio\Middleware\LazyLoadingMiddleware->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))" 1621877034521,#22 /etc/shlink/vendor/akrabat/ip-address-middleware/src/IpAddress.php(132): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest)) 1621877034521,"#23 /etc/shlink/vendor/mezzio/mezzio/src/Middleware/LazyLoadingMiddleware.php(41): RKA\Middleware\IpAddress->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))" 1621877034521,"#24 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Mezzio\Middleware\LazyLoadingMiddleware->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))" 1621877034521,#25 /etc/shlink/vendor/laminas/laminas-stratigility/src/MiddlewarePipe.php(84): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest)) 1621877034521,"#26 /etc/shlink/vendor/mezzio/mezzio-router/src/Route.php(85): Laminas\Stratigility\MiddlewarePipe->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))" 1621877034521,"#27 /etc/shlink/vendor/mezzio/mezzio-router/src/RouteResult.php(97): Mezzio\Router\Route->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))" 1621877034521,"#28 /etc/shlink/vendor/mezzio/mezzio-router/src/Middleware/DispatchMiddleware.php(30): Mezzio\Router\RouteResult->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))" 1621877034521,"#29 /etc/shlink/vendor/mezzio/mezzio/src/Middleware/LazyLoadingMiddleware.php(41): Mezzio\Router\Middleware\DispatchMiddleware->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))" 1621877034521,"#30 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Mezzio\Middleware\LazyLoadingMiddleware->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))" 1621877034521,#31 /etc/shlink/vendor/laminas/laminas-stratigility/src/MiddlewarePipe.php(84): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest)) 1621877034521,"#32 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Laminas\Stratigility\MiddlewarePipe->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))" 1621877034521,#33 /etc/shlink/vendor/laminas/laminas-stratigility/src/Middleware/PathMiddlewareDecorator.php(48): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest)) 1621877034521,"#34 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Laminas\Stratigility\Middleware\PathMiddlewareDecorator->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))" 1621877034521,#35 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(54): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest)) 1621877034521,#36 /etc/shlink/vendor/mezzio/mezzio-router/src/Middleware/ImplicitHeadMiddleware.php(76): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest)) 1621877034521,"#37 /etc/shlink/vendor/mezzio/mezzio/src/Middleware/LazyLoadingMiddleware.php(41): Mezzio\Router\Middleware\ImplicitHeadMiddleware->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))" 1621877034521,"#38 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Mezzio\Middleware\LazyLoadingMiddleware->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))" 1621877034521,#39 /etc/shlink/vendor/mezzio/mezzio-router/src/Middleware/RouteMiddleware.php(50): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest)) 1621877034521,"#40 /etc/shlink/vendor/mezzio/mezzio/src/Middleware/LazyLoadingMiddleware.php(41): Mezzio\Router\Middleware\RouteMiddleware->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))" 1621877034521,"#41 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Mezzio\Middleware\LazyLoadingMiddleware->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))" 1621877034521,#42 /etc/shlink/vendor/laminas/laminas-stratigility/src/MiddlewarePipe.php(84): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest)) 1621877034521,"#43 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Laminas\Stratigility\MiddlewarePipe->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))" 1621877034521,#44 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(54): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest)) 1621877034521,#45 /etc/shlink/vendor/shlinkio/shlink-common/src/Middleware/CloseDbConnectionMiddleware.php(27): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest)) 1621877034521,"#46 /etc/shlink/vendor/mezzio/mezzio/src/Middleware/LazyLoadingMiddleware.php(41): Shlinkio\Shlink\Common\Middleware\CloseDbConnectionMiddleware->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))" 1621877034521,"#47 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Mezzio\Middleware\LazyLoadingMiddleware->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))" 1621877034521,#48 /etc/shlink/vendor/laminas/laminas-stratigility/src/MiddlewarePipe.php(84): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest)) 1621877034521,"#49 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Laminas\Stratigility\MiddlewarePipe->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))" 1621877034521,#50 /etc/shlink/vendor/laminas/laminas-stratigility/src/Middleware/PathMiddlewareDecorator.php(48): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest)) 1621877034521,"#51 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Laminas\Stratigility\Middleware\PathMiddlewareDecorator->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))" 1621877034521,#52 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(54): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest)) 1621877034521,#53 /etc/shlink/vendor/laminas/laminas-stratigility/src/Middleware/ErrorHandler.php(144): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest)) 1621877034521,"#54 /etc/shlink/vendor/mezzio/mezzio/src/Middleware/LazyLoadingMiddleware.php(41): Laminas\Stratigility\Middleware\ErrorHandler->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))" 1621877034521,"#55 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Mezzio\Middleware\LazyLoadingMiddleware->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))" 1621877034521,#56 /etc/shlink/vendor/shlinkio/shlink-common/src/Middleware/ContentLengthMiddleware.php(28): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest)) 1621877034521,"#57 /etc/shlink/vendor/mezzio/mezzio/src/Middleware/LazyLoadingMiddleware.php(41): Shlinkio\Shlink\Common\Middleware\ContentLengthMiddleware->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))" 1621877034521,"#58 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Mezzio\Middleware\LazyLoadingMiddleware->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))" 1621877034521,#59 /etc/shlink/vendor/laminas/laminas-stratigility/src/MiddlewarePipe.php(84): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest)) 1621877034521,"#60 /etc/shlink/vendor/laminas/laminas-stratigility/src/Next.php(61): Laminas\Stratigility\MiddlewarePipe->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\Next))" 1621877034521,#61 /etc/shlink/vendor/laminas/laminas-stratigility/src/MiddlewarePipe.php(84): Laminas\Stratigility\Next->handle(Object(Laminas\Diactoros\ServerRequest)) 1621877034521,"#62 /etc/shlink/vendor/laminas/laminas-stratigility/src/MiddlewarePipe.php(73): Laminas\Stratigility\MiddlewarePipe->process(Object(Laminas\Diactoros\ServerRequest), Object(Laminas\Stratigility\EmptyPipelineHandler))" 1621877034521,#63 /etc/shlink/vendor/mezzio/mezzio-swoole/src/Event/RequestHandlerRequestListener.php(121): Laminas\Stratigility\MiddlewarePipe->handle(Object(Laminas\Diactoros\ServerRequest)) 1621877034521,#64 /etc/shlink/vendor/mezzio/mezzio-swoole/src/Event/EventDispatcher.php(41): Mezzio\Swoole\Event\RequestHandlerRequestListener->__invoke(Object(Mezzio\Swoole\Event\RequestEvent)) 1621877034521,#65 /etc/shlink/vendor/mezzio/mezzio-swoole/src/SwooleRequestHandlerRunner.php(142): Mezzio\Swoole\Event\EventDispatcher->dispatch(Object(Mezzio\Swoole\Event\RequestEvent)) 1621877034521,#66 {main}

    bug 
    opened by rumeau 21
  • Run in a subfolder with nginx

    Run in a subfolder with nginx

    I'd like to run shlink in a subfolder with nginx. I can't seem to come up with the right location combination. Has anyone else tried this?

    For example, http://example.com/go/

    question meta-issue 
    opened by poblabs 21
  • Unable to upgrade from 2.10.2 > 3.0.0

    Unable to upgrade from 2.10.2 > 3.0.0

    I seem to be having a similar issue when upgrading from 2.10.2 > 3.0.0

    How Shlink is set-up Shlink Version: 3.0.0 PHP Version: 8.0.14 How do you serve Shlink: Self-hosted, Apache 2.4.4, Ubuntu 20.04 LTS Database engine used: SQLite

    Summary Trying to upgrade from 2.10.2 to 3.0.0 and v3 is unable to migrate v2 database.

    Behavior

    link@seattle:~/domains/serve.link.nwadventists.com$ ll
    total 1304
    drwxr-x--- 12 link link    4096 Feb  7 16:14 ./
    drwxr-xr-x  4 link link    4096 Feb  6 02:03 ../
    -rw-r--r--  1 link link      44 Feb  6 01:49 .virtualmin-src
    drwxr-x---  2 link link    4096 Aug 10 12:34 cgi-bin/
    drwxrwxr-x  2 link link    4096 Feb  7 15:59 data/
    drwxr-xr-x  4 link link    4096 Aug 10 12:34 etc/
    drwxr-xr-x  2 link link    4096 Jul 21  2021 fcgi-bin/
    drwxr-xr-x  2 link link    4096 Sep  1  2020 homes/
    drwxr-x---  2 link link    4096 Sep  1  2020 logs/
    -rw-rw-r--  1 link link 1253376 May 24  2021 original_database.sqlite
    lrwxrwxrwx  1 link link      13 Sep  1  2020 public_html -> shlink/public/
    drwxrwxr-x  8 link link    4096 Feb  7 15:38 shlink/
    drwxrwxr-x  8 link link    4096 Feb  7 15:38 shlink-old-old/
    drwxr-xr-x  8 link link    4096 Jan  7 12:55 shlink2.10.2_php8.0_dist/
    -rwxr-xr-x  1 link link    3750 Dec 12 15:07 ssl.ca*
    -rwx------  1 link link    2264 Dec 12 15:07 ssl.cert*
    -rwx------  1 link link    6016 Dec 12 15:07 ssl.combined*
    -rwx------  1 link link    7725 Dec 12 15:07 ssl.everything*
    -rwx------  1 link link    1708 Dec 12 15:07 ssl.key*
    drwxr-x---  2 link link    4096 Sep  1  2020 tmp/
    link@seattle:~/domains/serve.link.nwadventists.com$ wget https://github.com/shlinkio/shlink/releases/download/v3.0.0/shlink3.0.0_php8.0_dist.zip && unzip shlink3.0.0_php8.0_dist.zip && rm shlink3.0.0_php8.0_dist.zip && mv shlink/ shlink-old/ && cp -ap shlink3.0.0_php8.0_dist/ shlink/ && cd shlink/ && vendor/bin/shlink-installer update -vvv
    --2022-02-07 16:18:15--  https://github.com/shlinkio/shlink/releases/download/v3.0.0/shlink3.0.0_php8.0_dist.zip
    Resolving github.com (github.com)... 140.82.113.3
    Connecting to github.com (github.com)|140.82.113.3|:443... connected.
    HTTP request sent, awaiting response... 302 Found
    Location: https://objects.githubusercontent.com/github-production-release-asset-2e65be/55801807/d38f8fc6-0532-447c-9411-57fb23181c7e?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20220208%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20220208T001815Z&X-Amz-Expires=300&X-Amz-Signature=bc10f0b7362843a3b4342a018258b15710559a41be68ae705154cd1e74495a0d&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=55801807&response-content-disposition=attachment%3B%20filename%3Dshlink3.0.0_php8.0_dist.zip&response-content-type=application%2Foctet-stream [following]
    --2022-02-07 16:18:15--  https://objects.githubusercontent.com/github-production-release-asset-2e65be/55801807/d38f8fc6-0532-447c-9411-57fb23181c7e?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20220208%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20220208T001815Z&X-Amz-Expires=300&X-Amz-Signature=bc10f0b7362843a3b4342a018258b15710559a41be68ae705154cd1e74495a0d&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=55801807&response-content-disposition=attachment%3B%20filename%3Dshlink3.0.0_php8.0_dist.zip&response-content-type=application%2Foctet-stream
    Resolving objects.githubusercontent.com (objects.githubusercontent.com)... 185.199.108.133, 185.199.109.133, 185.199.110.133, ...
    Connecting to objects.githubusercontent.com (objects.githubusercontent.com)|185.199.108.133|:443... connected.
    HTTP request sent, awaiting response... 200 OK
    Length: 22311182 (21M) [application/octet-stream]
    Saving to: ‘shlink3.0.0_php8.0_dist.zip’
    
    shlink3.0.0_php8.0_dist.zip    100%[===================================================>]  21.28M  44.6MB/s    in 0.5s    
    
    2022-02-07 16:18:16 (44.6 MB/s) - ‘shlink3.0.0_php8.0_dist.zip’ saved [22311182/22311182]
    
    Archive:  shlink3.0.0_php8.0_dist.zip
    
     Welcome to Shlink!!
     This tool will guide you through the installation process.
    
     Do you want to import configuration from previous installation? (You will still be asked for any new config option that did not exist in previous shlink versions) (yes/no) [yes]:
     > yes
    
     Previous shlink installation path from which to import config:
     > ../shlink-old/
    
    DATABASE
    ========
    
     Database name [shlink]:
     > 
    
    

    The CLI accepts the previous instance path, but then it asks to setup the database, though one already exists. Completing the CLI questions results in a broken install and database not migrated. If I tried to enter other paths, but the CLI will tell me those aren't valid, so obviously my initial path is correct.

    Originally posted by @antwonw in https://github.com/shlinkio/shlink/issues/1357#issuecomment-1032087525

    bug 
    opened by antwonw 19
  • YOURLS Import: SQL errors 'Invalid datetime format'

    YOURLS Import: SQL errors 'Invalid datetime format'

    Hi there again,

    just tried to re-setup shlink (latest version) and finally get my old YOURLS installation imported. I had already successfully test-imported another YOURLS instance some weeks ago with your plugin enabled, which didn't produce those errors. Unfortunately the import now seems to go through, but at the end of importing there are three error messages thrown by the importer concerning datetime format:

    In ExceptionConverter.php line 119:

    An exception occurred while executing a query: SQLSTATE[22007]: Invalid datetime format: 1366 Incorrect string valu e: '\xF0\x9F\xA6\x94' for column shlink.short_urls.title at row 1

    In Exception.php line 26:

    SQLSTATE[22007]: Invalid datetime format: 1366 Incorrect string value: '\xF0\x9F\xA6\x94' for column shlink.shor t_urls.title at row 1

    In Statement.php line 95:

    SQLSTATE[22007]: Invalid datetime format: 1366 Incorrect string value: '\xF0\x9F\xA6\x94' for column shlink.shor t_urls.title at row 1

    Here is what I do:

    1. Install shlink and set my future shlink main domain, e.g. myshorts.com (currently pointed to a YOURLS instance), as the default domain. No errors.
    2. Point a different domain to the shlink installation and add it to your web interface, e.g. tempdomain.com. Adds successfully.
    3. Starting the import. Import seems to get and import all URLs, only to stop with above error messages. Database as well as the web interface show that none of the URLs and visits have been imported.

    Did I do something wrong along the way?

    bug 
    opened by enoversum 19
  • Import via YOURLS API

    Import via YOURLS API

    It's already possible, via a plugin, to export data from YOURLS as CSV and import the data to Shlink accordingly. Given that statistics cannot be transferred through CSV, a native YOURLS import would be helpful to not lose tracking stats. YOURLS themselves have an API to support that: https://yourls.org/#API .

    feature 
    opened by enoversum 19
  • Support multiple domains

    Support multiple domains

    Considerations:

    • This feature must be backwards compatible. Any existing installation should keep working with currently configured domain.
    • [x] The domain that is currently provided to shlink will be considered the default one.
    • [x] It should be possible to create the same custom slug for different domains (which means the unique key currently set to the short_code column should be changed to use short_code+domain_id).
    • [x] Short codes will respond following these rules:
      • Request with unknown domain or default domain -> will redirect if the shortcode exists for default domain. 404 otherwise.
      • Request with know domain other than default one -> will redirect if shortcode is found for that domain or the default one. 404 otherwise.
    • [x] Domains should become a new entity.
    • [x] Domains will be created on the fly when new short URLs are created providing a specific domain (either via REST or CLI)
    • [x] Endpoints and commands to create short URLs should allow optionally providing the domain.
    • [x] When creating a short URL with findIfExists flag, it should try to match domain too, if provided.

    Improvements for future versions:

    • Allow to define different redirects for 404 errors on each one of the domains.
    • Allow short codes to be restricted to one or multiple domains, failing to redirect instead of falling back to default one.
    • Provide CLI commands and REST actions to create and list domains.
    • Allow searching short URLs by domain name (including default domain, which value is not really stored)

    Other affected projects:

    • Shlink installer: https://github.com/shlinkio/shlink-installer/issues/21
    • Shlink web client: https://github.com/shlinkio/shlink-web-client/issues/144
    feature 
    opened by acelaya 19
  • Allow limiting access only to short URLs created with the same API key.

    Allow limiting access only to short URLs created with the same API key.

    Summary

    Hello, there is a feature like different api key and then differenct short link they have ? cause i dont see it in documentation and if not it will be great if there is

    i mean, now the api key that i made for the same domain it's still can seeing the other url with different api key

    it's possible ?

    Thanks before

    feature meta-issue 
    opened by captainAldi 17
  • MSSQL support

    MSSQL support

    Summary

    Hi,

    This project looks really interesting. I was wondering if you were planning to add support for MSSQL at any point soon? MSSQL inside Azure for example is considerably cheaper than MySQL/MariaDB/PostgreSQL from both Azure and AWS' PaaS offerings.

    I did try and get it working myself but discovered there was quite a few places I'd need to hook up to make it work and I'm obviously not too familiar with how the project

    Thanks.


    • [x] Document support for MSSQL.
    • [x] Add database tests running on MSSQL.
    • [x] Make installer allow selecting it as one of the available databases.
    • [x] Add driver to docker image.
    • [x] Document how to use it with docker image.
    • [x] Add it to GitHub issue templates.
    • [ ] ~~Make tests against MSSQL to be run on travis, if possible.~~ Will be handled in #657
    feature 
    opened by akiller 17
  • Daily GeoIP Database Download Limit Reached

    Daily GeoIP Database Download Limit Reached

    hi,

    i got an email from geoip and when I check the download history, it looks strange to me:

    image

    so, besides the question my shrink is trying to download that often, I am more interested in "how can I disable the geoip stuff in general?" because I don´t really need it. i just made the setup to see if that brings any valuable info for me, but it doesn't.

    thanks!

    bug question 
    opened by michabbb 16
  • Show message before redirect / Ask for confirmation before redirect

    Show message before redirect / Ask for confirmation before redirect

    (I'm not quite sure if the web client repo is the right place for this, so please feel free to transfer! )

    Summary

    Wouldn't it be a neat feature to set an option for certain shortlinks so the user who clicks on them needs to actively confirm he wants to get redirected? Not only for possible NSFW related content. But also for affiliate links where you need some sort of consent from the user (mostly because of GDPR).

    feature 
    opened by CodeAlDente 2
  • Consider using swagger-php to automatically generated OpenAPI docs

    Consider using swagger-php to automatically generated OpenAPI docs

    https://zircote.github.io/swagger-php/guide/annotations.html

    Back in the day, I made a conscious decision of manually writing the swagger/open API spec.

    The reason is that I always felt tooling that generates them automatically, frequently requires annotating your source code with API docs info that pollutes it and mixes/spreads docs inside your source code.

    However, very often I find myself almost forgetting to document changes due to the docs being separated from the source code, or even worried that I might make a mistake and document something incorrectly and not matching the actual behavior.

    Because of this I want to do a POC, using swagger-php to document one endpoint and see how it feels, what are the pros and cons of both options and try to decide if manual documentation is still the way to go or not.

    • [ ] Document the decision on an architectural decision record.
    experimental docs 
    opened by acelaya 0
  • Different long URL based on device type.

    Different long URL based on device type.

    I would love it if we could specify different (long) URLS based on device type. This would be super useful for setting up a short url to download an app. Shlink would detect the device the user visits from, redirect on android would go to the play store, and the redirect on iOS would go to the app store. A 3rd URL could be added for any device that isn't iOS or android - which could go to a landing page. Perhaps this could even be built into Shlink. We could specify a message to be displayed if the user is visiting from an "unsupported" device. These settings could all be in the "advanced options".

    feature 
    opened by snorkrat 6
  • Author only API key not displaying all tags after server update to 3.2.1

    Author only API key not displaying all tags after server update to 3.2.1

    How Shlink is set-up

    • Shlink Version: 3.2.1
    • PHP Version: 8.1.9
    • How do you serve Shlink: Self-hosted nginx
    • Database engine used: MariaDB

    Summary

    After updating to Shlink verison 3.2.1, an API "author only" API key we use only shows 1 tag, when looking at the "Manage tags" area of app.shlink.io.

    Current behavior

    There is only one tag displaying in app.shlink.io under "Manage tags", where there should be 20+ image

    If you look at the "List short URLs", you can see the other tags listed in the "Tags" column image

    Expected behavior

    All tags should display and be manageable from the "Manage tags" area of app.shlink.io

    How to reproduce

    This happened when I created an author only api key, created several tags and short links, then updated the server to version 3.2.1.

    Thank you for looking into this, and please let me know if I can help in any way.

    bug 
    opened by LINCCLIBS 12
  • Support a different default domain behavior

    Support a different default domain behavior

    Summary

    Shlink currently has a not super predictable behavior regarding the default domain.

    This is because it used to support only one domain, and when multi-domain support was added, it had to maintain backwards compatibility.

    However, this behavior could be changed for users coming to Shlink now, via a config option that tells how it should behave.

    See this discussion for details.

    feature 🥇 gold 
    opened by acelaya 0
Releases(v3.4.0)
Owner
Shlink. The self-hosted URL shortener
null
Laravel based API to shorten URLs and share them easily. Redirects to the real URL by entering a short URL generated by the API

URL Shortener Requirements: PHP 7.4 or above composer node / npm Installation clone the project from the Github repository, enter the project folder,

Julio Vergara 5 Nov 20, 2021
A URL shortener with various other utilities, backed by a custom lightweight framework.

da.gd What is da.gd? da.gd is both a URL shortener and a collection of quick-info tools written in PHP. It allows you to use curl (or any http client)

da.gd 76 Jan 2, 2023
URL shortener web application based on the Laravel PHP Framework.

UrlHub Warning: UrlHub is still in development, constantly being optimized and isn't still stable enough to be used in production environments. Whatev

Kei 349 Jan 4, 2023
URL shortener web application based on the Laravel PHP Framework.

UrlHub Warning: UrlHub is still in development, constantly being optimized and isn't still stable enough to be used in production environments. Whatev

Kei 348 Dec 23, 2022
URL - link shortener based on sqlite

link-url-shortener url - link shortener based on sqlite.

Okin On 1 Nov 12, 2021
A simple URL shortener for PHP

Shorty Shorty is a simple URL shortener for PHP. Installation 1. Download and extract the files to your web directory. 2. Use the included database.sq

Mike Cao 210 Dec 19, 2022
The modern, privacy-aware URL Shortener built in PHP.

About UrlHum UrlHum is a modern, privacy-aware and fast URL Shortener built with PHP and the Laravel Framework. At the moment UrlHum is heavily under

UrlHum 622 Jan 7, 2023
[DEPRECATED] Library for extraction of domain parts e.g. TLD. Domain parser that uses Public Suffix List

DEPRECATED Consider to use https://github.com/jeremykendall/php-domain-parser as maintained alternative. TLDExtract TLDExtract accurately separates th

Oleksandr Fediashov 216 Oct 18, 2022
:aerial_tramway: A modern, powerful, and robust URL shortener

?? A modern, minimalist, and lightweight URL shortener. Polr is an intrepid, self-hostable open-source link shortening web application with a robust A

Chaoyi Zha 4.6k Jan 1, 2023
A fast and powerful URL Shortener built with Laravel, VueJS, and Tailwind CSS.

A fast and powerful URL Shortener built with Laravel, VueJS, and Tailwind CSS.

Devpri 53 Dec 25, 2022
Simpler Url Shortener for Laravel

Laravel Url Shortener Install composer require magarrent/laravel-url-shortener Run migrations: php artisan migrate Configuration If you want to config

Marc Garcia Torrent 51 Dec 17, 2022
A simple but powerful URL shortener

UrlShorter 这是一个足够简洁的Url短网址生成器 This is a simple Url shorter. 兼容性 在PHP7.X 与 PHP 8.0 下测试通过 安装 step 1: git clone [email protected]:soxft/UrlShorter.git step

xcsoft 74 Dec 21, 2022
A modern, powerful, and robust URL shortener

?? A modern, minimalist, and lightweight URL shortener. Polr is an intrepid, self-hostable open-source link shortening web application with a robust A

Chaoyi Zha 4.6k Dec 30, 2022
Laravel URL Localization Manager - [ccTLD, sub-domain, sub-directory].

Laravel URL Localization - (ccTLD, sub-domain, sub-directory). with Simple & Easy Helpers. Afrikaans Akan shqip አማርኛ العربية հայերեն অসমীয়া azərbayca

Pharaonic 2 Aug 7, 2022
Um encurtador de URL's gratuito e Open source. Torne suas URL's um tanto pequenas forma rápida e gratuita

Slim Framework 4 Skeleton Application Use this skeleton application to quickly setup and start working on a new Slim Framework 4 application. This app

Kilderson Sena 30 Jun 30, 2022
Public Suffix List based domain parsing implemented in PHP

PHP Domain Parser PHP Domain Parser is a resource based domain parser implemented in PHP. Motivation While there are plenty of excellent URL parsers a

Jeremy Kendall 1k Jan 1, 2023
A simple PHP library to parse and manipulate URLs

Url is a simple library to ease creating and managing Urls in PHP.

The League of Extraordinary Packages 351 Dec 30, 2022
A laravel package for generating Bitly short URLs.

Laravel Bitly Package A laravel package for generating Bitly short URLs. For more information see Bitly Requirements Laravel 5.1 or later Installation

Wessel Strengholt 72 Nov 8, 2022
Create and validate signed URLs with a limited lifetime

THIS PACKAGE IS NOT MAINTAINED ANYMORE. SIGNING URLS IS NOW PART OF LARAVEL: https://laravel-news.com/signed-routes Create secured URLs with a limited

Spatie 652 Dec 31, 2022