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: [email protected]
          traefik.http.routers.api.rule: PathPrefix(`/api/`) || PathPrefix(`/dashboard/`) || Path(`/api`) || Path(`/dashboard`)
          traefik.http.routers.api.service: [email protected]
        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

    [email protected]:~/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/
    [email protected]:~/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
  • Add ability to exclude bots from visitCount in short-urls endpoint

    Add ability to exclude bots from visitCount in short-urls endpoint

    Summary

    For each short url, the short-urls endpoint returns a count of the number of visits a redirect has seen, in visitsCount. We created a short url and posted it to Mastodon. The way Mastodon works is each individual instance that sees a post goes and fetches the metadata for links in the post for itself. So a link posted to a reasonably popular account can immediately see >1k visits to a link from automated crawlers, and the number short-urls returns ends up being way higher than the number of humans that have clicked on the link.

    The user agent of these requests makes it pretty clear they're from Mastodon, and Shlink correctly identifies the requests as potentialBot in the visits list.

    It would be great if it were possible to pass a parameter, maybe reusing excludeBots from the visits api endpoint, to a GET request to the short-urls endpoint that would cause shlink to to ignore the potential bots when calculating the returned visitsCount. Alternately, a 2nd number could be calculated and returned, e.g. potentialNonBotVisitsCount.

    feature 
    opened by simonft 0
  • Support credentials on redis servers connection

    Support credentials on redis servers connection

    How Shlink is set-up

    • Shlink Version: v3.3.2
    • How do you serve Shlink: Docker image - roadrunner
    • Database engine used: PostgreSQL

    Summary

    Can not input Redis URI with password

    Current behavior

    It show this error:

    image

    Expected behavior

    Could input the password and database to the uri of Redis

    How to reproduce

    Create a docker compose file with service like this:

    shlink: container_name: shlink image: shlinkio/shlink:latest-roadrunner restart: always environment: - DEFAULT_DOMAIN:$SHORT_LINK - IS_HTTPS_ENABLED:false - GEOLITE_LICENSE_KEY:$SHLINK_GEOLITE_LICENSE_KEY - DB_DRIVER=postgres - DB_USER=$SHLINK_DB_USER - DB_PASSWORD=$SHLINK_DB_PASSWORD - DB_HOST=postgres - DB_PORT=5432 - ENABLE_PERIODIC_VISIT_LOCATE=true # - REDIS_SERVERS=$SHLINK_REDIS_SERVERS - REDIS_SERVERS=redis://[email protected]:6379/2

    feature 
    opened by khanhtranchi 2
  • Docker image improvements

    Docker image improvements

    • [ ] Copy composer from composer-bin image to reduce build times.
    • [ ] Check if shlink-installer can be removed from the project, together with the installer.global.php config file (Shlink should not depend directly on the package).
    enhancement 
    opened by acelaya 0
  • Can not generate API keys with Roles

    Can not generate API keys with Roles

    How Shlink is set-up

    • Shlink Version: 3.3.2
    • PHP Version: 8.1
    • How do you serve Shlink: Docker image
    • Database engine used: PostgreSQL 14

    Summary

    I would like to create a domain specific API Key but I always get an exception

    Current behavior

    bin/cli api-key:generate --domain-only=example.com
    
    Fatal error: Uncaught Error: Object of class Shlinkio\Shlink\Rest\ApiKey\Role could not be converted to string in /etc/shlink/vendor/doctrine/dbal/src/Driver/PDO/Statement.php:56
    Stack trace:
    #0 /etc/shlink/vendor/doctrine/dbal/src/Driver/PDO/Statement.php(56): PDOStatement->bindValue(1, Object(Shlinkio\Shlink\Rest\ApiKey\Role), 2)
    #1 /etc/shlink/vendor/doctrine/dbal/src/Statement.php(115): Doctrine\DBAL\Driver\PDO\Statement->bindValue(1, Object(Shlinkio\Shlink\Rest\ApiKey\Role), 2)
    #2 /etc/shlink/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php(274): Doctrine\DBAL\Statement->bindValue(1, Object(Shlinkio\Shlink\Rest\ApiKey\Role), Object(Doctrine\DBAL\Types\StringType))
    #3 /etc/shlink/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php(1129): Doctrine\ORM\Persisters\Entity\BasicEntityPersister->executeInserts()
    #4 /etc/shlink/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php(430): Doctrine\ORM\UnitOfWork->executeInserts(Object(Doctrine\ORM\Mapping\ClassMetadata))
    #5 /etc/shlink/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php(403): Doctrine\ORM\UnitOfWork->commit(NULL)
    #6 /etc/shlink/vendor/doctrine/orm/lib/Doctrine/ORM/Decorator/EntityManagerDecorator.php(211): Doctrine\ORM\EntityManager->flush(NULL)
    #7 /etc/shlink/module/Rest/src/Service/ApiKeyService.php(33): Doctrine\ORM\Decorator\EntityManagerDecorator->flush()
    #8 /etc/shlink/module/CLI/src/Command/Api/GenerateKeyCommand.php(96): Shlinkio\Shlink\Rest\Service\ApiKeyService->create(NULL, NULL, Object(Shlinkio\Shlink\Rest\ApiKey\Model\RoleDefinition))
    #9 /etc/shlink/vendor/symfony/console/Command/Command.php(308): Shlinkio\Shlink\CLI\Command\Api\GenerateKeyCommand->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
    #10 /etc/shlink/vendor/symfony/console/Application.php(1002): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
    #11 /etc/shlink/vendor/symfony/console/Application.php(299): Symfony\Component\Console\Application->doRunCommand(Object(Shlinkio\Shlink\CLI\Command\Api\GenerateKeyCommand), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
    #12 /etc/shlink/vendor/symfony/console/Application.php(171): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
    #13 /etc/shlink/bin/cli(10): Symfony\Component\Console\Application->run()
    #14 {main}
      thrown in /etc/shlink/vendor/doctrine/dbal/src/Driver/PDO/Statement.php on line 56
    

    My guess would be that doctrine has problems with converting the enum to a string field in the database. Creating keys without a role works without any problems.

    If you would like to have some debug statements at some place, let me know and I'll try them.

    Thank you

    bug 
    opened by juergenthomann 4
  • Short link created from web interface (https://app.shlink.io) doesn't work

    Short link created from web interface (https://app.shlink.io) doesn't work

    How Shlink is set-up

    • Shlink Version: 3.3.2
    • PHP Version: 8.1
    • How do you serve Shlink: Self-hosted nginx
    • Database engine used: MySQL (8.0.29-21 Percona Server)

    Summary

    Short link created from web interface (https://app.shlink.io) doesn't work and returns "An unexpected error occurred" when opening url

    Current behavior

    What I found that

    • link created from app.shlink.io has author_api_key_id in DB
    • link created from cli has author_api_key_id == null and it works fine

    When I run $ bin/cli short-url:list

    Get: PHP Warning: require(data/proxies/__CG__ShlinkioShlinkRestEntityApiKey.php): Failed to open stream: No such file or directory in /var/www/73n.ru/vendor/doctrine/common/src/Proxy/AbstractProxyFactory.php on line 197 PHP Fatal error: Uncaught Error: Failed opening required 'data/proxies/__CG__ShlinkioShlinkRestEntityApiKey.php' (include_path='.:/usr/share/php') in /var/www/73n.ru/vendor/doctrine/common/src/Proxy/AbstractProxyFactory.php:197 Stack trace: #0 /var/www/73n.ru/vendor/doctrine/common/src/Proxy/AbstractProxyFactory.php(109): Doctrine\Common\Proxy\AbstractProxyFactory->getProxyDefinition() #1 /var/www/73n.ru/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php(2873): Doctrine\Common\Proxy\AbstractProxyFactory->getProxy() #2 /var/www/73n.ru/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php(266): Doctrine\ORM\UnitOfWork->createEntity() #3 /var/www/73n.ru/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php(492): Doctrine\ORM\Internal\Hydration\ObjectHydrator->getEntity() #4 /var/www/73n.ru/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php(148): Doctrine\ORM\Internal\Hydration\ObjectHydrator->hydrateRowData() #5 /var/www/73n.ru/vendor/doctrine/orm/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php(270): Doctrine\ORM\Internal\Hydration\ObjectHydrator->hydrateAllData() #6 /var/www/73n.ru/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php(1224): Doctrine\ORM\Internal\Hydration\AbstractHydrator->hydrateAll() #7 /var/www/73n.ru/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php(1165): Doctrine\ORM\AbstractQuery->executeIgnoreQueryCache() #8 /var/www/73n.ru/vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php(901): Doctrine\ORM\AbstractQuery->execute() #9 /var/www/73n.ru/module/Core/src/ShortUrl/Repository/ShortUrlRepository.php(45): Doctrine\ORM\AbstractQuery->getResult() #10 /var/www/73n.ru/module/Core/src/ShortUrl/Paginator/Adapter/ShortUrlRepositoryAdapter.php(26): Shlinkio\Shlink\Core\ShortUrl\Repository\ShortUrlRepository->findList() #11 /var/www/73n.ru/vendor/pagerfanta/core/Pagerfanta.php(260): Shlinkio\Shlink\Core\ShortUrl\Paginator\Adapter\ShortUrlRepositoryAdapter->getSlice() #12 /var/www/73n.ru/vendor/pagerfanta/core/Pagerfanta.php(246): Pagerfanta\Pagerfanta->getCurrentPageResultsFromAdapter() #13 /var/www/73n.ru/vendor/pagerfanta/core/Pagerfanta.php(415): Pagerfanta\Pagerfanta->getCurrentPageResults() #14 /var/www/73n.ru/vendor/lstrojny/functional-php/src/Functional/Map.php(31): Pagerfanta\Pagerfanta->getIterator() #15 /var/www/73n.ru/module/CLI/src/Command/ShortUrl/ListShortUrlsCommand.php(173): Functional\map() #16 /var/www/73n.ru/module/CLI/src/Command/ShortUrl/ListShortUrlsCommand.php(147): Shlinkio\Shlink\CLI\Command\ShortUrl\ListShortUrlsCommand->renderPage() #17 /var/www/73n.ru/vendor/symfony/console/Command/Command.php(308): Shlinkio\Shlink\CLI\Command\ShortUrl\ListShortUrlsCommand->execute() #18 /var/www/73n.ru/vendor/symfony/console/Application.php(1002): Symfony\Component\Console\Command\Command->run() #19 /var/www/73n.ru/vendor/symfony/console/Application.php(299): Symfony\Component\Console\Application->doRunCommand() #20 /var/www/73n.ru/vendor/symfony/console/Application.php(171): Symfony\Component\Console\Application->doRun() #21 /var/www/73n.ru/bin/cli(10): Symfony\Component\Console\Application->run() #22 {main} thrown in /var/www/73n.ru/vendor/doctrine/common/src/Proxy/AbstractProxyFactory.php on line 197

    Also some other commands return errors in CLI:

    $bin/cli visit:non-orphan

    PHP Warning: require(data/proxies/__CG__ShlinkioShlinkCoreShortUrlEntityShortUrl.php): Failed to open stream: No such file or directory in /var/www/73n.ru/vendor/doctrine/common/src/Proxy/AbstractProxyFactory.php on line 197 PHP Fatal error: Uncaught Error: Failed opening required 'data/proxies/__CG__ShlinkioShlinkCoreShortUrlEntityShortUrl.php' (include_path='.:/usr/share/php') in /var/www/73n.ru/vendor/doctrine/common/src/Proxy/AbstractProxyFactory.php:197

    Something hasn't installed properly I guess, but how to finish this set up?

    bug 
    opened by Ext7 1
Releases(v3.3.2)
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 74 Nov 13, 2022
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 344 Nov 26, 2022
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 346 Dec 2, 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 207 Nov 3, 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 617 Nov 23, 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.5k Dec 6, 2022
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 52 Sep 16, 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 49 Aug 9, 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 72 Nov 13, 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.5k Dec 3, 2022
[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
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 Nov 28, 2022
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 352 Nov 17, 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 648 Dec 3, 2022