Compare commits

...

100 Commits

Author SHA1 Message Date
Alejandro Celaya
3479bbbb36 Merge pull request #567 from acelaya-forks/hotfix/v1.20.2
Hotfix/v1.20.2
2019-12-06 23:09:20 +01:00
Alejandro Celaya
e1a1a0652f Merge pull request #566 from acelaya-forks/bugfix/date-parsing
Bugfix/date parsing
2019-12-06 22:52:22 +01:00
Alejandro Celaya
3e9b775114 Fixed failing test 2019-12-06 22:45:15 +01:00
Alejandro Celaya
57c91aca3c Updated changelog with v1.20.2 2019-12-06 22:40:08 +01:00
Alejandro Celaya
05a64b8d9e Ensured dates parsing does not mask actual validation errors 2019-12-06 22:38:22 +01:00
Alejandro Celaya
30780f9c5f Merge pull request #565 from acelaya-forks/bugfix/control-rename-tag
Bugfix/control rename tag
2019-12-06 21:13:54 +01:00
Alejandro Celaya
3455df9214 Updated changelog 2019-12-06 21:06:47 +01:00
Alejandro Celaya
27aa8f9875 Handled rename tag error from command 2019-12-06 21:04:52 +01:00
Alejandro Celaya
05451e3d1a Handled tag conflict from rename tag action 2019-12-06 21:03:27 +01:00
Alejandro Celaya
b9b3295b52 Ensured a specific exception is thrown from TagService when trying to rename a tag to the name of another tag which already exists 2019-12-06 20:44:41 +01:00
Alejandro Celaya
f62ed66e26 Created TagConflictException 2019-12-06 10:20:56 +01:00
Alejandro Celaya
e2a9a989ab Merge pull request #563 from acelaya-forks/bugfix/missing-yaml
Bugfix/missing yaml
2019-12-06 09:59:09 +01:00
Alejandro Celaya
4af27650cd Updated changelog 2019-12-06 09:52:23 +01:00
Alejandro Celaya
76a603104d Migrated migrations config file from yaml to plain PHP 2019-12-06 09:50:37 +01:00
Alejandro Celaya
115ca0da0f Added v1.20.1 to changelog 2019-11-17 11:29:54 +01:00
Alejandro Celaya
673b545a83 Merge pull request #551 from acelaya-forks/feature/non-shared-locker
Feature/non shared locker
2019-11-17 11:21:27 +01:00
Alejandro Celaya
d030fd1aa6 Updated GeolocationDbUpdater to always use a local lock even if redis config is provided 2019-11-17 11:09:37 +01:00
Alejandro Celaya
7c1e40be88 Updated docker docs regarding image versioning 2019-11-17 10:38:05 +01:00
Alejandro Celaya
b739619532 Merge pull request #550 from acelaya-forks/feature/fix-db-silent-errors
Feature/fix db silent errors
2019-11-17 10:08:57 +01:00
Alejandro Celaya
372b83d92f Updated changelog 2019-11-17 10:02:03 +01:00
Alejandro Celaya
4e3b5419d5 Created small helper composer command 2019-11-17 10:00:29 +01:00
Alejandro Celaya
c34d5a35e2 Updated database commands so that internal commands are run with mustRun 2019-11-17 09:52:45 +01:00
Alejandro Celaya
a959b5bf02 Merge pull request #549 from acelaya-forks/feature/use-own-test-domains
Replaced third party domains used in tests by custom shlink domains
2019-11-16 13:47:37 +01:00
Alejandro Celaya
45ac2c3c51 Replaced third party domains used in tests by custom shlink domains 2019-11-16 13:37:53 +01:00
Alejandro Celaya
f6bddc6f24 Merge pull request #548 from acelaya-forks/feature/redirect-to-idn
Handled IDN domains also on internal redirections when validating a URL
2019-11-16 12:46:02 +01:00
Alejandro Celaya
6b8fc3228e Handled IDN domains also on internal redirections when validating a URL 2019-11-16 12:38:45 +01:00
Alejandro Celaya
8cf1a95df5 Swoole is no longer experimental 2019-11-16 10:59:56 +01:00
Alejandro Celaya
b3ea2969c5 Merge pull request #547 from acelaya-forks/feature/support-idn
Feature/support idn
2019-11-16 10:32:49 +01:00
Alejandro Celaya
054bbb8d5a Updated changelog 2019-11-16 10:22:00 +01:00
Alejandro Celaya
19c1b29f59 Added tests for UrlValidator 2019-11-16 10:19:25 +01:00
Alejandro Celaya
264b8c2a9e Added support for IDN 2019-11-16 10:06:55 +01:00
Alejandro Celaya
ec33b95f97 Brought intl extension back to docker images and kept as a requirement 2019-11-16 09:46:42 +01:00
Alejandro Celaya
4437d5305f Merge pull request #546 from acelaya-forks/feature/image-version
Feature/image version
2019-11-15 22:24:42 +01:00
Alejandro Celaya
f20f01e22e Removed Intl from docker image 2019-11-15 22:23:07 +01:00
Alejandro Celaya
1ee30fe5dc Updated dev docker images 2019-11-15 22:05:34 +01:00
Alejandro Celaya
4dc026d7fc Merge pull request #544 from Starbix/master
Update dependencies and baseimage
2019-11-15 22:01:14 +01:00
Cédric Laubacher
1e862a8ee8 Readd specific alpine version 2019-11-15 21:42:07 +01:00
Alejandro Celaya
5ece2d1939 Merge pull request #539 from acelaya-forks/feature/forward-query
Feature/forward query
2019-11-15 20:43:11 +01:00
Alejandro Celaya
146e9100be Updated changelog 2019-11-15 20:30:36 +01:00
Cédric Laubacher
0c854edc6b Use specific PHP version 2019-11-15 19:16:29 +01:00
Cédric Laubacher
07d031e7b9 Update Dockerfile 2019-11-15 17:55:06 +01:00
Alejandro Celaya
705dc2ec39 Added forward of query string from short URLs to long one 2019-11-13 21:04:44 +01:00
Alejandro Celaya
3b9221c7d2 Ensured options for short.url:list command have required values 2019-11-13 20:24:59 +01:00
Alejandro Celaya
576d602ed0 Merge pull request #537 from acelaya-forks/feature/installer-3.1
Updated to installer 3.1
2019-11-10 13:15:30 +01:00
Alejandro Celaya
9df8bd63d4 Updated to installer 3.1 2019-11-10 13:07:57 +01:00
Alejandro Celaya
99c4802367 Fixed docker docs line break 2019-11-10 12:14:00 +01:00
Alejandro Celaya
94dc6f2053 Merge pull request #536 from acelaya-forks/feature/simplified-config-workers
Added workers nums handling to simplified config parser
2019-11-10 12:11:41 +01:00
Alejandro Celaya
d4005da35c Added workers nums handling to simplified config parser 2019-11-10 12:04:14 +01:00
Alejandro Celaya
cbe2c362d5 Merge pull request #535 from acelaya-forks/feature/api-test-script
Updated API tests script so that it throws the same exit code returne…
2019-11-09 12:15:09 +01:00
Alejandro Celaya
8bf79db66a Fixed typo 2019-11-09 12:08:22 +01:00
Alejandro Celaya
b87964f716 Updated API tests script so that it throws the same exit code returned by phpunit 2019-11-09 11:25:33 +01:00
Alejandro Celaya
b0a574f578 Merge pull request #533 from acelaya-forks/feature/custom-workers
Feature/custom workers
2019-11-09 11:17:03 +01:00
Alejandro Celaya
92dc3019de Updated changelog 2019-11-09 11:08:28 +01:00
Alejandro Celaya
d8f92cb2be Added web worker num and task worker num to docker image config 2019-11-09 11:05:54 +01:00
Alejandro Celaya
bf24660ddb Merge pull request #526 from acelaya-forks/feature/enhanced-not-found-redirect
Feature/enhanced not found redirect
2019-11-02 21:24:34 +01:00
Alejandro Celaya
b66268867a Updated to shlink-installer 3 2019-11-02 21:15:26 +01:00
Alejandro Celaya
9abaa243e0 Some extra minor improvements 2019-11-02 19:08:07 +01:00
Alejandro Celaya
7030138ff4 Updated changelog 2019-11-02 18:57:16 +01:00
Alejandro Celaya
906dfe60f8 Simplified code to render not-foubnd templates by infering the template to be used inside NotFoundHandler 2019-11-02 18:49:24 +01:00
Alejandro Celaya
01f60614ef Removed hardcoded route names for core routes and used action class names instead 2019-11-02 18:36:15 +01:00
Alejandro Celaya
eeb5306883 Moved all logic to redirect to specific URLs when a 404 is found to the NotFoundHandler 2019-11-02 18:33:26 +01:00
Alejandro Celaya
24c3a3e84c Defined new options for new redirect configuration 2019-11-02 17:23:21 +01:00
Alejandro Celaya
8b9663aea0 Created DeprecatedConfigParserTest 2019-11-02 17:04:49 +01:00
Alejandro Celaya
b59f4e2805 Defined new configs for not found redirects 2019-11-02 11:30:09 +01:00
Alejandro Celaya
6293d57fde Merge pull request #523 from acelaya-forks/feature/refactorings
Feature/refactorings
2019-11-01 17:31:43 +01:00
Alejandro Celaya
39ac2efe26 Updated to latest shlink-common with bug fixes 2019-11-01 17:16:56 +01:00
Alejandro Celaya
1f449e8ce1 Disabled coroutines on swoole during API tests 2019-11-01 10:10:43 +01:00
Alejandro Celaya
ad906000c7 Removed typehint making phpstan throw false positive 2019-11-01 10:04:25 +01:00
Alejandro Celaya
5361f33cc1 Some more refactorings 2019-11-01 09:52:56 +01:00
Alejandro Celaya
1937f3ea22 Trying to automatically persist tags 2019-10-26 09:01:51 +02:00
Alejandro Celaya
f4e9d0c8fe Merge pull request #517 from acelaya-forks/feature/too-long-lock
Updated to shlink-ip-geolocation v1.1
2019-10-25 20:12:54 +02:00
Alejandro Celaya
9b767ee9f3 Updated to shlink-ip-geolocation v1.1 2019-10-25 20:00:26 +02:00
Alejandro Celaya
43cb91bf52 Merge pull request #516 from acelaya-forks/feature/qr-code-with-domain
Feature/qr code with domain
2019-10-22 20:03:37 +02:00
Alejandro Celaya
f784ee5b28 Fixed unit tests 2019-10-22 19:52:28 +02:00
Alejandro Celaya
cd6f067fe5 Ensured domain is taken into account when generating QR codes 2019-10-22 19:43:53 +02:00
Alejandro Celaya
6d366188c9 Added github funding 2019-10-22 19:37:35 +02:00
Alejandro Celaya
c4ca59dcb0 Merge pull request #515 from acelaya-forks/feature/host-and-port
Feature/host and port
2019-10-20 11:14:44 +02:00
Alejandro Celaya
74675ad314 Updated changelog 2019-10-20 10:30:51 +02:00
Alejandro Celaya
b5e4da847a Allowed port number on domain field when creating shotr URLs 2019-10-20 10:30:11 +02:00
Alejandro Celaya
232bf5a68b Merge pull request #513 from acelaya-forks/feature/fix-long-urls
Feature/fix long urls
2019-10-20 10:15:38 +02:00
Alejandro Celaya
67958a78d3 Updated changelog 2019-10-20 09:55:00 +02:00
Alejandro Celaya
b8cdc29d8f Increased long URL size in DB to 2048 characters 2019-10-20 09:53:11 +02:00
Alejandro Celaya
30e4ddb950 Updated to latest infection version 2019-10-20 09:42:46 +02:00
Alejandro Celaya
a61a7db275 Merge pull request #510 from acelaya-forks/feature/update-common
Updated common and qr-code
2019-10-15 19:17:44 +02:00
Alejandro Celaya
0f5e4e7fa2 Updated common and qr-code 2019-10-15 19:06:37 +02:00
Alejandro Celaya
eb17eae781 Merge pull request #506 from acelaya-forks/feature/improved-shortcodes
Feature/improved shortcodes
2019-10-11 12:51:24 +02:00
Alejandro Celaya
740a65f880 Updated references to SHORTCODE_CHARS in docker docs 2019-10-11 11:41:14 +02:00
Alejandro Celaya
5bd7b53e0a Added more tests for new logics 2019-10-11 11:28:53 +02:00
Alejandro Celaya
9538f474de Added logic to check if a short code is in use and regenerate it otherwise 2019-10-11 11:09:33 +02:00
Alejandro Celaya
8f2e78c946 Moved logic to generate random short codes to external function 2019-10-11 09:35:09 +02:00
Alejandro Celaya
2f09ff456c Updated logic to generate random short codes, increasing entropy 2019-10-11 09:14:25 +02:00
Alejandro Celaya
c8d950e04d Merge pull request #504 from acelaya-forks/feature/mariadb
Feature/mariadb
2019-10-06 12:37:22 +02:00
Alejandro Celaya
0f6a0da7a4 Added MariaDB to documentation 2019-10-06 12:27:11 +02:00
Alejandro Celaya
4300fb225f Ensured tests on maria DB are not run on travis, since it would conflict with MySQL 2019-10-06 12:23:02 +02:00
Alejandro Celaya
159021e87c Added MariaDB to issue templates 2019-10-06 11:39:53 +02:00
Alejandro Celaya
9dc6ea9eeb Documented how to use maria db with docker image 2019-10-06 11:38:23 +02:00
Alejandro Celaya
42e84e526e Added mariadb container for development environment 2019-10-06 11:21:41 +02:00
Alejandro Celaya
700ee40109 Merge pull request #503 from acelaya-forks/feature/update-coding-standard
Feature/update coding standard
2019-10-05 18:07:12 +02:00
Alejandro Celaya
4909bd9550 Updated changelog 2019-10-05 17:29:07 +02:00
Alejandro Celaya
e0d20bf8ff Updated to coding styles v2 2019-10-05 17:26:10 +02:00
342 changed files with 1893 additions and 686 deletions

View File

@@ -3,7 +3,6 @@ APP_ENV=
SECRET_KEY=
SHORTENED_URL_SCHEMA=
SHORTENED_URL_HOSTNAME=
SHORTCODE_CHARS=
# Database
DB_USER=

1
.github/FUNDING.yml vendored
View File

@@ -1 +1,2 @@
github: ['acelaya']
custom: ['https://acel.me/donate']

View File

@@ -18,7 +18,7 @@ With that said, please fill in the information requested next. More information
* Shlink Version: x.y.z
* PHP Version: x.y.z
* How do you serve Shlink: Self-hosted Apache|Self-hosted nginx|Self-hosted swoole|Docker image
* Database engine used: MySQL|PostgreSQL|SQLite (x.y.z)
* Database engine used: MySQL|MariaDB|PostgreSQL|SQLite (x.y.z)
#### Summary

View File

@@ -18,7 +18,7 @@ With that said, please fill in the information requested next. More information
* Shlink Version: x.y.z
* PHP Version: x.y.z
* How do you serve Shlink: Self-hosted Apache|Self-hosted nginx|Self-hosted swoole|Docker image
* Database engine used: MySQL|PostgreSQL|SQLite (x.y.z)
* Database engine used: MySQL|MariaDB|PostgreSQL|SQLite (x.y.z)
#### Summary

View File

@@ -18,6 +18,10 @@ services:
- postgresql
- docker
cache:
directories:
- $HOME/.composer/cache/files
before_install:
- echo 'extension = apcu.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
- yes | pecl install swoole
@@ -25,7 +29,7 @@ before_install:
install:
- composer self-update
- composer install --no-interaction
- composer install --no-interaction --prefer-dist
before_script:
- mysql -e 'CREATE DATABASE shlink_test;'

View File

@@ -4,6 +4,97 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com), and this project adheres to [Semantic Versioning](https://semver.org).
## 1.20.2 - 2019-12-06
#### Added
* *Nothing*
#### Changed
* *Nothing*
#### Deprecated
* *Nothing*
#### Removed
* *Nothing*
#### Fixed
* [#561](https://github.com/shlinkio/shlink/issues/561) Fixed `db:migrate` command failing because yaml extension is not installed, which makes config file not to be readable.
* [#562](https://github.com/shlinkio/shlink/issues/562) Fixed internal server error being returned when renaming a tag to another tag's name. Now a meaningful API error with status 409 is returned.
* [#555](https://github.com/shlinkio/shlink/issues/555) Fixed internal server error being returned when invalid dates are provided for new short URLs. Now a 400 is returned, as intended.
## 1.20.1 - 2019-11-17
#### Added
* [#519](https://github.com/shlinkio/shlink/issues/519) Documented how to customize web workers and task workers for the docker image.
#### Changed
* *Nothing*
#### Deprecated
* *Nothing*
#### Removed
* *Nothing*
#### Fixed
* [#512](https://github.com/shlinkio/shlink/issues/512) Fixed query params not being properly forwarded from short URL to long one.
* [#540](https://github.com/shlinkio/shlink/issues/540) Fixed errors thrown when creating short URLs if the original URL has an internationalized domain name and URL validation is enabled.
* [#528](https://github.com/shlinkio/shlink/issues/528) Ensured `db:create` and `db:migrate` commands do not silently fail when run as part of `install` or `update`.
* [#518](https://github.com/shlinkio/shlink/issues/518) Fixed service which updates Geolite db file to use a local lock instead of a shared one, since every shlink instance holds its own db instance.
## 1.20.0 - 2019-11-02
#### Added
* [#491](https://github.com/shlinkio/shlink/issues/491) Added improved short code generation logic.
Now, short codes are truly random, which removes the guessability factor existing in previous versions.
Generated short codes have 5 characters, and shlink makes sure they keep unique, while making it backwards-compatible.
* [#418](https://github.com/shlinkio/shlink/issues/418) and [#419](https://github.com/shlinkio/shlink/issues/419) Added support to redirect any 404 error to a custom URL.
It was already possible to configure this but only for invalid short URLs. Shlink now also support configuring redirects for the base URL and any other kind of "not found" error.
The three URLs can be different, and it is already possible to pass them to the docker image via configuration or env vars.
The installer also asks for these two new configuration options.
* [#497](https://github.com/shlinkio/shlink/issues/497) Officially added support for MariaDB.
#### Changed
* [#458](https://github.com/shlinkio/shlink/issues/458) Updated coding styles to use [shlinkio/php-coding-standard](https://github.com/shlinkio/php-coding-standard) v2.0.0.
#### Deprecated
* *Nothing*
#### Removed
* *Nothing*
#### Fixed
* [#507](https://github.com/shlinkio/shlink/issues/507) Fixed error with too long original URLs by increasing size to the maximum value (2048) based on [the standard](https://stackoverflow.com/a/417184).
* [#502](https://github.com/shlinkio/shlink/issues/502) Fixed error when providing the port as part of the domain on short URLs.
* [#509](https://github.com/shlinkio/shlink/issues/509) Fixed error when trying to generate a QR code for a short URL which uses a custom domain.
* [#522](https://github.com/shlinkio/shlink/issues/522) Highly mitigated errors thrown when lots of short URLs are created concurrently including new and existing tags.
## 1.19.0 - 2019-10-05
#### Added

View File

@@ -1,10 +1,10 @@
FROM php:7.3.8-cli-alpine3.10
FROM php:7.3.11-alpine3.10
LABEL maintainer="Alejandro Celaya <alejandro@alejandrocelaya.com>"
ARG SHLINK_VERSION=1.18.1
ARG SHLINK_VERSION=1.20.0
ENV SHLINK_VERSION ${SHLINK_VERSION}
ENV SWOOLE_VERSION 4.3.3
ENV COMPOSER_VERSION 1.9.0
ENV SWOOLE_VERSION 4.4.12
ENV COMPOSER_VERSION 1.9.1
WORKDIR /etc/shlink
@@ -17,7 +17,7 @@ RUN \
# Install postgres
apk add --no-cache postgresql-dev && \
docker-php-ext-install -j"$(nproc)" pdo_pgsql && \
# [Deprecated] Install intl
# Install intl
apk add --no-cache icu-dev && \
docker-php-ext-install -j"$(nproc)" intl && \
# Install zip and gd

View File

@@ -22,7 +22,7 @@ A PHP-based self-hosted URL shortener that can be used to serve shortened URLs u
First make sure the host where you are going to run shlink fulfills these requirements:
* PHP 7.2 or greater with JSON, APCu, intl, curl, PDO and gd extensions enabled.
* MySQL, PostgreSQL or SQLite.
* MySQL, MariaDB, PostgreSQL or SQLite.
* The web server of your choice with PHP integration (Apache or Nginx recommended).
Then, you will need a built version of the project. There are a few ways to get it.
@@ -49,7 +49,7 @@ Then, you will need a built version of the project. There are a few ways to get
Despite how you built the project, you are going to need to install it now, by following these steps:
* If you are going to use MySQL or PostgreSQL, create an empty database with the name of your choice.
* If you are going to use MySQL, MariaDB or PostgreSQL, 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.**
* Expose shlink to the web, either by using a traditional web server + fast CGI approach, or by using a [swoole](https://www.swoole.co.uk/) non-blocking server.
@@ -103,8 +103,6 @@ Despite how you built the project, you are going to need to install it now, by f
* **Using swoole:**
**Important!** Swoole support is still experimental. Use it with care, and report any found issue.
First you need to install the swoole PHP extension with [pecl](https://pecl.php.net/package/swoole), `pecl install swoole`.
Once installed, it's actually pretty easy to get shlink up and running with swoole. Just run `./vendor/bin/zend-expressive-swoole start -d` and you will get shlink running on port 8080.

View File

@@ -1,7 +1,6 @@
#!/usr/bin/env sh
set -e
export APP_ENV=test
export DB_DRIVER=mysql
# Try to stop server just in case it hanged in last execution
vendor/bin/zend-expressive-swoole stop
@@ -11,4 +10,9 @@ vendor/bin/zend-expressive-swoole start -d
sleep 2
vendor/bin/phpunit --order-by=random -c phpunit-api.xml --testdox --colors=always
testsExitCode=$?
vendor/bin/zend-expressive-swoole stop
# Exit this script with the same code as the tests. If tests failed, this script has to fail
exit $testsExitCode

View File

@@ -23,7 +23,7 @@
"doctrine/dbal": "^2.9",
"doctrine/migrations": "^2.0",
"doctrine/orm": "^2.5",
"endroid/qr-code": "^1.7",
"endroid/qr-code": "^3.6",
"firebase/php-jwt": "^4.0",
"geoip2/geoip2": "^2.9",
"guzzlehttp/guzzle": "^6.3",
@@ -33,10 +33,11 @@
"ocramius/proxy-manager": "~2.2.2",
"phly/phly-event-dispatcher": "^1.0",
"predis/predis": "^1.1",
"shlinkio/shlink-common": "^2.0",
"pugx/shortid-php": "^0.5",
"shlinkio/shlink-common": "^2.2.1",
"shlinkio/shlink-event-dispatcher": "^1.0",
"shlinkio/shlink-installer": "^2.0",
"shlinkio/shlink-ip-geolocation": "^1.0",
"shlinkio/shlink-installer": "^3.1",
"shlinkio/shlink-ip-geolocation": "^1.1",
"symfony/console": "^4.3",
"symfony/filesystem": "^4.3",
"symfony/lock": "^4.3",
@@ -59,12 +60,12 @@
"devster/ubench": "^2.0",
"eaglewu/swoole-ide-helper": "dev-master",
"filp/whoops": "^2.4",
"infection/infection": "^0.13.4",
"phpstan/phpstan": "^0.11.2",
"infection/infection": "^0.14.2",
"phpstan/phpstan": "^0.11.16",
"phpunit/phpcov": "^6.0",
"phpunit/phpunit": "^8.3",
"roave/security-advisories": "dev-master",
"shlinkio/php-coding-standard": "~1.2.2",
"shlinkio/php-coding-standard": "~2.0.0",
"shlinkio/shlink-test-utils": "^1.0",
"symfony/dotenv": "^4.3",
"symfony/var-dumper": "^4.3",
@@ -77,7 +78,10 @@
"Shlinkio\\Shlink\\Rest\\": "module/Rest/src",
"Shlinkio\\Shlink\\Core\\": "module/Core/src",
"Shlinkio\\Shlink\\PreviewGenerator\\": "module/PreviewGenerator/src/"
}
},
"files": [
"module/Core/functions/functions.php"
]
},
"autoload-dev": {
"psr-4": {
@@ -98,11 +102,9 @@
"@test:ci",
"@infect:ci"
],
"cs": "phpcs",
"cs:fix": "phpcbf",
"stan": "phpstan analyse module/*/src/ module/*/config config docker/config --level=5 -c phpstan.neon",
"test": [
"@test:unit",
"@test:db",
@@ -110,37 +112,42 @@
],
"test:ci": [
"@test:unit:ci",
"@test:db",
"@test:db:ci",
"@test:api"
],
"test:unit": "phpdbg -qrr vendor/bin/phpunit --order-by=random --colors=always --coverage-php build/coverage-unit.cov --testdox",
"test:unit:ci": "phpdbg -qrr vendor/bin/phpunit --order-by=random --colors=always --coverage-php build/coverage-unit.cov --coverage-clover=build/clover.xml --coverage-xml=build/coverage-xml --log-junit=build/phpunit.junit.xml --testdox",
"test:db": [
"@test:db:sqlite",
"@test:db:mysql",
"@test:db:maria",
"@test:db:postgres"
],
"test:db:ci": [
"@test:db:sqlite",
"@test:db:mysql",
"@test:db:postgres"
],
"test:db:sqlite": "APP_ENV=test phpdbg -qrr vendor/bin/phpunit --order-by=random --colors=always -c phpunit-db.xml --coverage-php build/coverage-db.cov --testdox",
"test:db:mysql": "DB_DRIVER=mysql composer test:db:sqlite",
"test:db:maria": "DB_DRIVER=maria composer test:db:sqlite",
"test:db:postgres": "DB_DRIVER=postgres composer test:db:sqlite",
"test:api": "bin/test/run-api-tests.sh",
"test:pretty": [
"@test",
"phpdbg -qrr vendor/bin/phpcov merge build --html build/html"
],
"test:unit:pretty": "phpdbg -qrr vendor/bin/phpunit --order-by=random --colors=always --coverage-html build/coverage",
"infect": "infection --threads=4 --min-msi=75 --log-verbosity=default --only-covered",
"infect:ci": "infection --threads=4 --min-msi=75 --log-verbosity=default --only-covered --coverage=build",
"infect:show": "infection --threads=4 --min-msi=75 --log-verbosity=default --only-covered --show-mutations",
"infect:test": [
"@test:unit:ci",
"@infect:ci"
]
],
"clean:dev": "rm -f data/database.sqlite && rm -f config/params/generated_config.php"
},
"scripts-descriptions": {
"check": "<fg=blue;options=bold>Alias for \"cs\", \"stan\", \"test\" and \"infect\"</>",
"ci": "<fg=blue;options=bold>Alias for \"cs\", \"stan\", \"test:ci\" and \"infect:ci\"</>",
"cs": "<fg=blue;options=bold>Checks coding styles</>",
"cs:fix": "<fg=blue;options=bold>Fixes coding styles, when possible</>",
@@ -149,9 +156,11 @@
"test:ci": "<fg=blue;options=bold>Runs all test suites, generating all needed reports and logs for CI envs</>",
"test:unit": "<fg=blue;options=bold>Runs unit test suites</>",
"test:unit:ci": "<fg=blue;options=bold>Runs unit test suites, generating all needed reports and logs for CI envs</>",
"test:db": "<fg=blue;options=bold>Runs database test suites on a SQLite, MySQL and PostgreSQL</>",
"test:db": "<fg=blue;options=bold>Runs database test suites on a SQLite, MySQL, MariaDB and PostgreSQL</>",
"test:db:ci": "<fg=blue;options=bold>Runs database test suites on a SQLite, MySQL and PostgreSQL</>",
"test:db:sqlite": "<fg=blue;options=bold>Runs database test suites on a SQLite database</>",
"test:db:mysql": "<fg=blue;options=bold>Runs database test suites on a MySQL database</>",
"test:db:maria": "<fg=blue;options=bold>Runs database test suites on a MariaDB database</>",
"test:db:postgres": "<fg=blue;options=bold>Runs database test suites on a PostgreSQL database</>",
"test:api": "<fg=blue;options=bold>Runs API test suites</>",
"test:pretty": "<fg=blue;options=bold>Runs all test suites and generates an HTML code coverage report</>",
@@ -159,7 +168,8 @@
"infect": "<fg=blue;options=bold>Checks unit tests quality applying mutation testing</>",
"infect:ci": "<fg=blue;options=bold>Checks unit tests quality applying mutation testing with existing reports and logs</>",
"infect:show": "<fg=blue;options=bold>Checks unit tests quality applying mutation testing and shows applied mutators</>",
"infect:test": "<fg=blue;options=bold>Checks unit tests quality applying mutation testing</>"
"infect:test": "<fg=blue;options=bold>Checks unit tests quality applying mutation testing</>",
"clean:dev": "<fg=blue;options=bold>Deletes artifacts which are gitignored and could affect dev env</>"
},
"config": {
"sort-packages": true

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
use function Shlinkio\Shlink\Common\env;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
return [

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
use Zend\ConfigAggregator\ConfigAggregator;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
use Zend\Expressive;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\Common;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
return [

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
use Shlinkio\Shlink\Installer\Config\Plugin;
@@ -9,10 +10,7 @@ return [
Plugin\UrlShortenerConfigCustomizer::class => [
Plugin\UrlShortenerConfigCustomizer::SCHEMA,
Plugin\UrlShortenerConfigCustomizer::HOSTNAME,
Plugin\UrlShortenerConfigCustomizer::CHARS,
Plugin\UrlShortenerConfigCustomizer::VALIDATE_URL,
Plugin\UrlShortenerConfigCustomizer::ENABLE_NOT_FOUND_REDIRECTION,
Plugin\UrlShortenerConfigCustomizer::NOT_FOUND_REDIRECT_TO,
],
Plugin\ApplicationConfigCustomizer::class => [
@@ -21,6 +19,8 @@ return [
Plugin\ApplicationConfigCustomizer::CHECK_VISITS_THRESHOLD,
Plugin\ApplicationConfigCustomizer::VISITS_THRESHOLD,
Plugin\ApplicationConfigCustomizer::BASE_PATH,
Plugin\ApplicationConfigCustomizer::WEB_WORKER_NUM,
Plugin\ApplicationConfigCustomizer::TASK_WORKER_NUM,
],
Plugin\DatabaseConfigCustomizer::class => [
@@ -31,6 +31,12 @@ return [
Plugin\DatabaseConfigCustomizer::HOST,
Plugin\DatabaseConfigCustomizer::PORT,
],
Plugin\RedirectsConfigCustomizer::class => [
Plugin\RedirectsConfigCustomizer::INVALID_SHORT_URL_REDIRECT_TO,
Plugin\RedirectsConfigCustomizer::REGULAR_404_REDIRECT_TO,
Plugin\RedirectsConfigCustomizer::BASE_URL_REDIRECT_TO,
],
],
'installation_commands' => [

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
use Shlinkio\Shlink\Common\Cache\RedisFactory;
@@ -7,6 +8,10 @@ use Shlinkio\Shlink\Common\Logger\LoggerAwareDelegatorFactory;
use Symfony\Component\Lock;
use Zend\ServiceManager\AbstractFactory\ConfigAbstractFactory;
// This class alias tricks the ConfigAbstractFactory to return Lock\Factory instances even with a different service name
$localLockFactory = 'Shlinkio\Shlink\LocalLockFactory';
class_alias(Lock\Factory::class, $localLockFactory);
return [
'locks' => [
@@ -18,11 +23,14 @@ return [
Lock\Store\FlockStore::class => ConfigAbstractFactory::class,
Lock\Store\RedisStore::class => ConfigAbstractFactory::class,
Lock\Factory::class => ConfigAbstractFactory::class,
$localLockFactory => ConfigAbstractFactory::class,
],
'aliases' => [
// With this config, a user could alias 'lock_store' => 'redis_lock_store' to override the default
'lock_store' => Lock\Store\FlockStore::class,
'lock_store' => 'local_lock_store',
'redis_lock_store' => Lock\Store\RedisStore::class,
'local_lock_store' => Lock\Store\FlockStore::class,
],
'delegators' => [
Lock\Store\RedisStore::class => [
@@ -38,6 +46,7 @@ return [
Lock\Store\FlockStore::class => ['config.locks.locks_dir'],
Lock\Store\RedisStore::class => [RedisFactory::SERVICE_NAME],
Lock\Factory::class => ['lock_store'],
$localLockFactory => ['local_lock_store'],
],
];

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink;

View File

@@ -1,7 +1,8 @@
<?php
declare(strict_types=1);
/** @deprecated */
/* @deprecated */
return [
'preview_generation' => [

View File

@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
return [
'not_found_redirects' => [
'invalid_short_url' => null, // Formerly url_shortener.not_found_short_url.redirect_to
'regular_404' => null,
'base_url' => null,
],
];

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
use Zend\Expressive\Router\FastRouteRouter;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
return [

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
return [

View File

@@ -1,7 +1,6 @@
<?php
declare(strict_types=1);
use Shlinkio\Shlink\Core\Options\UrlShortenerOptions;
declare(strict_types=1);
use function Shlinkio\Shlink\Common\env;
@@ -12,12 +11,7 @@ return [
'schema' => env('SHORTENED_URL_SCHEMA', 'http'),
'hostname' => env('SHORTENED_URL_HOSTNAME'),
],
'shortcode_chars' => env('SHORTCODE_CHARS', UrlShortenerOptions::DEFAULT_CHARS),
'validate_url' => true,
'not_found_short_url' => [
'enable_redirection' => false,
'redirect_to' => null,
],
],
];

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
return [

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
use Doctrine\ORM\EntityManager;
@@ -6,8 +7,10 @@ use Doctrine\ORM\Tools\Console\ConsoleRunner;
use Psr\Container\ContainerInterface;
use Zend\ServiceManager\ServiceManager;
/** @var ContainerInterface|ServiceManager $container */
$container = include __DIR__ . '/container.php';
$em = $container->get(EntityManager::class);
return (function () {
/** @var ContainerInterface|ServiceManager $container */
$container = include __DIR__ . '/container.php';
$em = $container->get(EntityManager::class);
return ConsoleRunner::createHelperSet($em);
return ConsoleRunner::createHelperSet($em);
})();

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink;
@@ -30,4 +31,5 @@ return (new ConfigAggregator\ConfigAggregator([
], 'data/cache/app_config.php', [
Core\Config\SimplifiedConfigParser::class,
Core\Config\BasePathPrefixer::class,
Core\Config\DeprecatedConfigParser::class,
]))->getMergedConfig();

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
use Symfony\Component\Dotenv\Dotenv;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\TestUtils;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\TestUtils;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink;
@@ -15,40 +16,41 @@ use function sys_get_temp_dir;
$swooleTestingHost = '127.0.0.1';
$swooleTestingPort = 9999;
$buildDbConnection = function () {
$buildDbConnection = function (): array {
$driver = env('DB_DRIVER', 'sqlite');
$isCi = env('TRAVIS', false);
$getMysqlHost = function (string $driver) {
return sprintf('shlink_db%s', $driver === 'mysql' ? '' : '_maria');
};
switch ($driver) {
case 'sqlite':
return [
'driver' => 'pdo_sqlite',
'path' => sys_get_temp_dir() . '/shlink-tests.db',
];
case 'mysql':
return [
'driver' => 'pdo_mysql',
'host' => $isCi ? '127.0.0.1' : 'shlink_db',
'user' => 'root',
'password' => $isCi ? '' : 'root',
'dbname' => 'shlink_test',
'charset' => 'utf8',
'driverOptions' => [
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
],
];
case 'postgres':
return [
'driver' => 'pdo_pgsql',
'host' => $isCi ? '127.0.0.1' : 'shlink_db_postgres',
'user' => 'postgres',
'password' => $isCi ? '' : 'root',
'dbname' => 'shlink_test',
'charset' => 'utf8',
];
default:
return [];
}
$driverConfigMap = [
'sqlite' => [
'driver' => 'pdo_sqlite',
'path' => sys_get_temp_dir() . '/shlink-tests.db',
],
'mysql' => [
'driver' => 'pdo_mysql',
'host' => $isCi ? '127.0.0.1' : $getMysqlHost($driver),
'user' => 'root',
'password' => $isCi ? '' : 'root',
'dbname' => 'shlink_test',
'charset' => 'utf8',
'driverOptions' => [
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
],
],
'postgres' => [
'driver' => 'pdo_pgsql',
'host' => $isCi ? '127.0.0.1' : 'shlink_db_postgres',
'user' => 'postgres',
'password' => $isCi ? '' : 'root',
'dbname' => 'shlink_test',
'charset' => 'utf8',
],
];
$driverConfigMap['maria'] = $driverConfigMap['mysql'];
return $driverConfigMap[$driver] ?? [];
};
return [
@@ -64,6 +66,7 @@ return [
],
'zend-expressive-swoole' => [
'enable_coroutine' => false,
'swoole-http-server' => [
'host' => $swooleTestingHost,
'port' => $swooleTestingPort,
@@ -72,6 +75,7 @@ return [
'pid_file' => sys_get_temp_dir() . '/shlink-test-swoole.pid',
'worker_num' => 1,
'task_worker_num' => 1,
'enable_coroutine' => false,
],
],
],

2
data/infra/database_maria/.gitignore vendored Executable file
View File

@@ -0,0 +1,2 @@
*
!.gitignore

View File

@@ -1,9 +1,9 @@
FROM php:7.3.1-fpm-alpine3.8
FROM php:7.3.11-fpm-alpine3.10
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
ENV APCU_VERSION 5.1.16
ENV APCU_BC_VERSION 1.0.4
ENV XDEBUG_VERSION "2.7.0RC1"
ENV APCU_VERSION 5.1.18
ENV APCU_BC_VERSION 1.0.5
ENV XDEBUG_VERSION 2.8.0
RUN apk update
@@ -13,17 +13,17 @@ RUN docker-php-ext-install iconv
RUN docker-php-ext-install mbstring
RUN docker-php-ext-install calendar
RUN apk add --no-cache --virtual sqlite-libs
RUN apk add --no-cache --virtual sqlite-dev
RUN apk add --no-cache sqlite-libs
RUN apk add --no-cache sqlite-dev
RUN docker-php-ext-install pdo_sqlite
RUN apk add --no-cache --virtual icu-dev
RUN apk add --no-cache icu-dev
RUN docker-php-ext-install intl
RUN apk add --no-cache --virtual libzip-dev zlib-dev
RUN apk add --no-cache libzip-dev zlib-dev
RUN docker-php-ext-install zip
RUN apk add --no-cache --virtual libpng-dev
RUN apk add --no-cache libpng-dev
RUN docker-php-ext-install gd
RUN apk add --no-cache postgresql-dev

View File

@@ -1,9 +1,10 @@
FROM php:7.3.1-cli-alpine3.8
FROM php:7.3.11-alpine3.10
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
ENV APCU_VERSION 5.1.16
ENV APCU_BC_VERSION 1.0.4
ENV APCU_VERSION 5.1.18
ENV APCU_BC_VERSION 1.0.5
ENV INOTIFY_VERSION 2.0.0
ENV SWOOLE_VERSION 4.4.12
RUN apk update
@@ -13,17 +14,17 @@ RUN docker-php-ext-install iconv
RUN docker-php-ext-install mbstring
RUN docker-php-ext-install calendar
RUN apk add --no-cache --virtual sqlite-libs
RUN apk add --no-cache --virtual sqlite-dev
RUN apk add --no-cache sqlite-libs
RUN apk add --no-cache sqlite-dev
RUN docker-php-ext-install pdo_sqlite
RUN apk add --no-cache --virtual icu-dev
RUN apk add --no-cache icu-dev
RUN docker-php-ext-install intl
RUN apk add --no-cache --virtual libzip-dev zlib-dev
RUN apk add --no-cache libzip-dev zlib-dev
RUN docker-php-ext-install zip
RUN apk add --no-cache --virtual libpng-dev
RUN apk add --no-cache libpng-dev
RUN docker-php-ext-install gd
RUN apk add --no-cache postgresql-dev
@@ -66,7 +67,7 @@ RUN rm /tmp/inotify.tar.gz
# Install swoole
# First line fixes an error when installing pecl extensions. Found in https://github.com/docker-library/php/issues/233
RUN apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS && \
pecl install swoole && \
pecl install swoole-${SWOOLE_VERSION} && \
docker-php-ext-enable swoole && \
apk del .phpize-deps

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace ShlinkMigrations;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace ShlinkMigrations;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace ShlinkMigrations;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace ShlinkMigrations;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace ShlinkMigrations;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace ShlinkMigrations;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace ShlinkMigrations;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace ShlinkMigrations;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace ShlinkMigrations;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace ShlinkMigrations;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace ShlinkMigrations;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace ShlinkMigrations;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace ShlinkMigrations;

View File

@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace ShlinkMigrations;
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\SchemaException;
use Doctrine\Migrations\AbstractMigration;
final class Version20191020074522 extends AbstractMigration
{
/**
* @throws SchemaException
*/
public function up(Schema $schema): void
{
$this->getOriginalUrlColumn($schema)->setLength(2048);
}
/**
* @throws SchemaException
*/
public function down(Schema $schema): void
{
$this->getOriginalUrlColumn($schema)->setLength(1024);
}
/**
* @throws SchemaException
*/
private function getOriginalUrlColumn(Schema $schema): Column
{
return $schema->getTable('short_urls')->getColumn('original_url');
}
}

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace <namespace>;

View File

@@ -24,3 +24,9 @@ services:
volumes:
- /etc/passwd:/etc/passwd:ro
- /etc/group:/etc/group:ro
shlink_db_maria:
user: 1000:1000
volumes:
- /etc/passwd:/etc/passwd:ro
- /etc/group:/etc/group:ro

View File

@@ -24,6 +24,7 @@ services:
links:
- shlink_db
- shlink_db_postgres
- shlink_db_maria
- shlink_redis
shlink_swoole:
@@ -33,11 +34,13 @@ services:
dockerfile: ./data/infra/swoole.Dockerfile
ports:
- "8080:8080"
- "9001:9001"
volumes:
- ./:/home/shlink
links:
- shlink_db
- shlink_db_postgres
- shlink_db_maria
- shlink_redis
shlink_db:
@@ -65,6 +68,19 @@ services:
POSTGRES_DB: shlink
PGDATA: /var/lib/postgresql/data/pgdata
shlink_db_maria:
container_name: shlink_db_maria
image: mariadb:10.2
ports:
- "3308:3306"
volumes:
- ./:/home/shlink/www
- ./data/infra/database_maria:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: shlink
MYSQL_INITDB_SKIP_TZINFO: 1
shlink_redis:
container_name: shlink_redis
image: redis:5.0-alpine

View File

@@ -56,16 +56,16 @@ docker exec -it shlink_container shlink
The image comes with a working sqlite database, but in production you will probably want to usa a distributed database.
It is possible to use a set of env vars to make this shlink instance interact with an external MySQL or PostgreSQL database.
It is possible to use a set of env vars to make this shlink instance interact with an external MySQL, MariaDB or PostgreSQL database.
* `DB_DRIVER`: **[Mandatory]**. Use the value **mysql** or **postgres** to prevent the sqlite database to be used.
* `DB_DRIVER`: **[Mandatory]**. Use the value **mysql**, **maria** or **postgres** to prevent the sqlite database to be used.
* `DB_NAME`: [Optional]. The database name to be used. Defaults to **shlink**.
* `DB_USER`: **[Mandatory]**. The username credential for the database server.
* `DB_PASSWORD`: **[Mandatory]**. The password credential for the database server.
* `DB_HOST`: **[Mandatory]**. The host name of the server running the database engine.
* `DB_PORT`: [Optional]. The port in which the database service is running.
* Default value is based on the driver:
* **mysql** -> `3306`
* Default value is based on the value provided for `DB_DRIVER`:
* **mysql** or **maria** -> `3306`
* **postgres** -> `5432`
> PostgreSQL is supported since v1.16.1 of this image. Do not try to use it with previous versions.
@@ -92,18 +92,24 @@ This is the complete list of supported env vars:
* `SHORT_DOMAIN_HOST`: The custom short domain used for this shlink instance. For example **doma.in**.
* `SHORT_DOMAIN_SCHEMA`: Either **http** or **https**.
* `SHORTCODE_CHARS`: A charset to use when building short codes. Only needed when using more than one shlink instance ([Multi instance considerations](#multi-instance-considerations)).
* `DB_DRIVER`: **sqlite** (which is the default value), **mysql** or **postgres**.
* `DB_DRIVER`: **sqlite** (which is the default value), **mysql**, **maria** or **postgres**.
* `DB_NAME`: The database name to be used when using an external database driver. Defaults to **shlink**.
* `DB_USER`: The username credential to be used when using an external database driver.
* `DB_PASSWORD`: The password credential to be used when using an external database driver.
* `DB_HOST`: The host name of the database server when using an external database driver.
* `DB_PORT`: The port in which the database service is running when using an external database driver. Defaults to **3306**.
* `DB_PORT`: The port in which the database service is running when using an external database driver.
* Default value is based on the value provided for `DB_DRIVER`:
* **mysql** or **maria** -> `3306`
* **postgres** -> `5432`
* `DISABLE_TRACK_PARAM`: The name of a query param that can be used to visit short URLs avoiding the visit to be tracked. This feature won't be available if not value is provided.
* `DELETE_SHORT_URL_THRESHOLD`: The amount of visits on short URLs which will not allow them to be deleted. Defaults to `15`.
* `VALIDATE_URLS`: Boolean which tells if shlink should validate a status 20x (after following redirects) is returned when trying to shorten a URL. Defaults to `true`.
* `NOT_FOUND_REDIRECT_TO`: If a URL is provided here, when a user tries to access an invalid short URL, he/she will be redirected to this value. If this env var is not provided, the user will see a generic `404 - not found` page.
* `INVALID_SHORT_URL_REDIRECT_TO`: If a URL is provided here, when a user tries to access an invalid short URL, he/she will be redirected to this value. If this env var is not provided, the user will see a generic `404 - not found` page.
* `REGULAR_404_REDIRECT_TO`: If a URL is provided here, when a user tries to access a URL not matching any one supported by the router, he/she will be redirected to this value. If this env var is not provided, the user will see a generic `404 - not found` page.
* `BASE_URL_REDIRECT_TO`: If a URL is provided here, when a user tries to access Shlink's base URL, he/she will be redirected to this value. If this env var is not provided, the user will see a generic `404 - not found` page.
* `BASE_PATH`: The base path from which you plan to serve shlink, in case you don't want to serve it from the root of the domain. Defaults to `''`.
* `WEB_WORKER_NUM`: The amount of concurrent http requests this shlink instance will be able to server. Defaults to 16.
* `TASK_WORKER_NUM`: The amount of concurrent background tasks this shlink instance will be able to execute. Defaults to 16.
* `REDIS_SERVERS`: A comma-separated list of redis servers where Shlink locks are stored (locks are used to prevent some operations to be run more than once in parallel).
This is important when running more than one Shlink instance ([Multi instance considerations](#multi-instance-considerations)). If not provided, Shlink stores locks on every instance separately.
@@ -112,6 +118,9 @@ This is the complete list of supported env vars:
In the future, these redis servers could be used for other caching operations performed by shlink.
* `NOT_FOUND_REDIRECT_TO`: **Deprecated since v1.20 in favor of `INVALID_SHORT_URL_REDIRECT_TO`** If a URL is provided here, when a user tries to access an invalid short URL, he/she will be redirected to this value. If this env var is not provided, the user will see a generic `404 - not found` page.
* `SHORTCODE_CHARS`: **Ignored when using Shlink 1.20 or newer**. A charset to use when building short codes. Only needed when using more than one shlink instance ([Multi instance considerations](#multi-instance-considerations)).
An example using all env vars could look like this:
```bash
@@ -129,9 +138,13 @@ docker run \
-e DISABLE_TRACK_PARAM="no-track" \
-e DELETE_SHORT_URL_THRESHOLD=30 \
-e VALIDATE_URLS=false \
-e "NOT_FOUND_REDIRECT_TO=https://www.google.com" \
-e "INVALID_SHORT_URL_REDIRECT_TO=https://my-landing-page.com" \
-e "REGULAR_404_REDIRECT_TO=https://my-landing-page.com" \
-e "BASE_URL_REDIRECT_TO=https://my-landing-page.com" \
-e "REDIS_SERVERS=tcp://172.20.0.1:6379,tcp://172.20.0.2:6379" \
-e "BASE_PATH=/my-campaign" \
-e WEB_WORKER_NUM=64 \
-e TASK_WORKER_NUM=32 \
shlinkio/shlink
```
@@ -150,7 +163,12 @@ The whole configuration should have this format, but it can be split into multip
"short_domain_schema": "https",
"short_domain_host": "doma.in",
"validate_url": false,
"not_found_redirect_to": "https://my-landing-page.com",
"invalid_short_url_redirect_to": "https://my-landing-page.com",
"regular_404_redirect_to": "https://my-landing-page.com",
"base_url_redirect_to": "https://my-landing-page.com",
"base_path": "/my-campaign",
"web_worker_num": 64,
"task_worker_num": 32,
"redis_servers": [
"tcp://172.20.0.1:6379",
"tcp://172.20.0.2:6379"
@@ -162,12 +180,15 @@ The whole configuration should have this format, but it can be split into multip
"password": "123abc",
"host": "something.rds.amazonaws.com",
"port": "3306"
}
},
"not_found_redirect_to": "https://my-landing-page.com"
}
```
> This is internally parsed to how shlink expects the config. If you are using a version previous to 1.17.0, this parser is not present and you need to provide a config structure like the one [documented previously](https://github.com/shlinkio/shlink-docker-image/tree/v1.16.3#provide-config-via-volumes).
> The `not_found_redirect_to` option has been deprecated in v1.20. Use `invalid_short_url_redirect_to` instead (however, it will still work for backwards compatibility).
Once created just run shlink with the volume:
```bash
@@ -178,7 +199,13 @@ docker run --name shlink -p 8080:8080 -v ${PWD}/my/config/dir:/etc/shlink/config
These are some considerations to take into account when running multiple instances of shlink.
* The first time shlink is run, it generates a charset used to generate short codes, which is a shuffled base62 charset.
* Some operations performed by Shlink should never be run more than once at the same time (like creating the database for the first time, or downloading the GeoLite2 database). For this reason, Shlink uses a locking system.
However, these locks are locally scoped to each Shlink instance by default.
You can (and should) make the locks to be shared by all Shlink instances by using a redis server/cluster. Just define the `REDIS_SERVERS` env var with the list of servers.
* **Ignore this if using Shlink 1.20 or newer**. The first time shlink is run, it generates a charset used to generate short codes, which is a shuffled base62 charset.
If you are using several shlink instances, you will probably want all of them to use the same charset.
@@ -186,18 +213,12 @@ These are some considerations to take into account when running multiple instanc
If you don't do this, each shlink instance will use a different charset. However this shouldn't be a problem in practice, since the chances to get a collision will be very low.
* Some operations performed by Shlink should never be run more than once at the same time (like creating the database for the first time, or downloading the GeoLite2 database). For this reason, Shlink uses a locking system.
However, these locks are locally scoped to each Shlink instance by default.
You can (and should) make the locks to be shared by all Shlink instances by using a redis server/cluster. Just define the `REDIS_SERVERS` env var with the list of servers.
## Versions
Versions of this image match the shlink version it contains.
Versioning on this docker image works as follows:
For example, installing `shlinkio/shlink:1.15.0`, you will get an image containing shlink v1.15.0.
* `X.X.X`: when providing a specific version number, the image version will match the shlink version it contains. For example, installing `shlinkio/shlink:1.15.0`, you will get an image containing shlink v1.15.0.
* `stable`: always holds the latest stable tag. For example, if latest shlink version is 1.20.0, installing `shlinkio/shlink:stable`, you will get an image containing shlink v1.20.0
* `latest`: always holds the latest contents in master, and it's considered unstable and not suitable for production.
The `latest` docker tag always holds the latest contents in master, and it's considered unestable and not suitable for production.
> There are no official shlink images previous to v1.15.0.
> **Important**: The docker image was introduced with shlink v1.15.0, so there are no official images previous to that versions.

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink;
@@ -10,6 +11,7 @@ use function explode;
use function file_exists;
use function file_get_contents;
use function file_put_contents;
use function Functional\contains;
use function implode;
use function Shlinkio\Shlink\Common\env;
use function sprintf;
@@ -21,24 +23,24 @@ $helper = new class {
private const BASE62 = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
private const DB_DRIVERS_MAP = [
'mysql' => 'pdo_mysql',
'maria' => 'pdo_mysql',
'postgres' => 'pdo_pgsql',
];
private const DB_PORTS_MAP = [
'mysql' => '3306',
'maria' => '3306',
'postgres' => '5432',
];
/** @var string */
private $charset;
/** @var string */
private $secretKey;
public function __construct()
{
[$this->charset, $this->secretKey] = $this->initShlinkKeys();
[, $this->secretKey] = $this->initShlinkSecretKey();
}
private function initShlinkKeys(): array
private function initShlinkSecretKey(): array
{
$keysFile = sprintf('%s/shlink.keys', sys_get_temp_dir());
if (file_exists($keysFile)) {
@@ -46,29 +48,19 @@ $helper = new class {
}
$keys = [
env('SHORTCODE_CHARS', $this->generateShortcodeChars()),
env('SECRET_KEY', $this->generateSecretKey()),
'', // This was the SHORTCODE_CHARS. Kept as empty string for BC
env('SECRET_KEY', $this->generateSecretKey()), // Deprecated
];
file_put_contents($keysFile, implode(',', $keys));
return $keys;
}
private function generateShortcodeChars(): string
{
return str_shuffle(self::BASE62);
}
private function generateSecretKey(): string
{
return substr(str_shuffle(self::BASE62), 0, 32);
}
public function getShortcodeChars(): string
{
return $this->charset;
}
public function getSecretKey(): string
{
return $this->secretKey;
@@ -84,8 +76,8 @@ $helper = new class {
];
}
$driverOptions = $driver !== 'mysql' ? [] : [
// PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
$driverOptions = ! contains(['maria', 'mysql'], $driver) ? [] : [
// 1002 -> PDO::MYSQL_ATTR_INIT_COMMAND
1002 => 'SET NAMES utf8',
];
return [
@@ -99,13 +91,12 @@ $helper = new class {
];
}
public function getNotFoundConfig(): array
public function getNotFoundRedirectsConfig(): array
{
$notFoundRedirectTo = env('NOT_FOUND_REDIRECT_TO');
return [
'enable_redirection' => $notFoundRedirectTo !== null,
'redirect_to' => $notFoundRedirectTo,
'invalid_short_url' => env('INVALID_SHORT_URL_REDIRECT_TO', env('NOT_FOUND_REDIRECT_TO')),
'regular_404' => env('REGULAR_404_REDIRECT_TO'),
'base_url' => env('BASE_URL_REDIRECT_TO'),
];
}
};
@@ -133,11 +124,11 @@ return [
'schema' => env('SHORT_DOMAIN_SCHEMA', 'http'),
'hostname' => env('SHORT_DOMAIN_HOST', ''),
],
'shortcode_chars' => $helper->getShortcodeChars(),
'validate_url' => (bool) env('VALIDATE_URLS', true),
'not_found_short_url' => $helper->getNotFoundConfig(),
],
'not_found_redirects' => $helper->getNotFoundRedirectsConfig(),
'logger' => [
'handlers' => [
'shlink_rotating_handler' => [
@@ -172,4 +163,13 @@ return [
'base_path' => env('BASE_PATH', ''),
],
'zend-expressive-swoole' => [
'swoole-http-server' => [
'options' => [
'worker_num' => (int) env('WEB_WORKER_NUM', 16),
'task_worker_num' => (int) env('TASK_WORKER_NUM', 16),
],
],
],
];

11
migrations.php Normal file
View File

@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
return [
'name' => 'ShlinkMigrations',
'migrations_namespace' => 'ShlinkMigrations',
'table_name' => 'migrations',
'migrations_directory' => 'data/migrations',
'custom_template' => 'data/migrations_template.txt',
];

View File

@@ -1,5 +0,0 @@
name: ShlinkMigrations
migrations_namespace: ShlinkMigrations
table_name: migrations
migrations_directory: data/migrations
custom_template: data/migrations_template.txt

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI;
@@ -57,7 +58,7 @@ return [
],
ConfigAbstractFactory::class => [
GeolocationDbUpdater::class => [DbUpdater::class, Reader::class, Locker::class],
GeolocationDbUpdater::class => [DbUpdater::class, Reader::class, 'Shlinkio\Shlink\LocalLockFactory'],
Command\ShortUrl\GenerateShortUrlCommand::class => [Service\UrlShortener::class, 'config.url_shortener.domain'],
Command\ShortUrl\ResolveUrlCommand::class => [Service\UrlShortener::class],

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Api;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Api;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Api;

View File

@@ -1,10 +1,10 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Config;
use Shlinkio\Shlink\CLI\Util\ExitCodes;
use Shlinkio\Shlink\Core\Options\UrlShortenerOptions;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@@ -17,6 +17,7 @@ use function str_shuffle;
class GenerateCharsetCommand extends Command
{
public const NAME = 'config:generate-charset';
private const DEFAULT_CHARS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
protected function configure(): void
{
@@ -25,14 +26,14 @@ class GenerateCharsetCommand extends Command
->setDescription(sprintf(
'[DEPRECATED] Generates a character set sample just by shuffling the default one, "%s". '
. 'Then it can be set in the SHORTCODE_CHARS environment variable',
UrlShortenerOptions::DEFAULT_CHARS
self::DEFAULT_CHARS
))
->setHelp('<fg=red;options=bold>This command is deprecated. Better leave shlink generate the charset.</>');
}
protected function execute(InputInterface $input, OutputInterface $output): ?int
{
$charSet = str_shuffle(UrlShortenerOptions::DEFAULT_CHARS);
$charSet = str_shuffle(self::DEFAULT_CHARS);
(new SymfonyStyle($input, $output))->success(sprintf('Character set: "%s"', $charSet));
return ExitCodes::EXIT_SUCCESS;
}

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Config;

View File

@@ -1,9 +1,11 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Db;
use Shlinkio\Shlink\CLI\Command\Util\AbstractLockedCommand;
use Shlinkio\Shlink\CLI\Command\Util\LockedCommandConfig;
use Symfony\Component\Console\Helper\ProcessHelper;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Lock\Factory as Locker;
@@ -28,6 +30,11 @@ abstract class AbstractDatabaseCommand extends AbstractLockedCommand
protected function runPhpCommand(OutputInterface $output, array $command): void
{
array_unshift($command, $this->phpBinary);
$this->processHelper->run($output, $command, null, null, $output->getVerbosity());
$this->processHelper->mustRun($output, $command);
}
protected function getLockConfig(): LockedCommandConfig
{
return new LockedCommandConfig($this->getName(), true);
}
}

View File

@@ -1,10 +1,10 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Db;
use Doctrine\DBAL\Connection;
use Shlinkio\Shlink\CLI\Command\Util\LockedCommandConfig;
use Shlinkio\Shlink\CLI\Util\ExitCodes;
use Symfony\Component\Console\Helper\ProcessHelper;
use Symfony\Component\Console\Input\InputInterface;
@@ -18,8 +18,8 @@ use function Functional\contains;
class CreateDatabaseCommand extends AbstractDatabaseCommand
{
public const NAME = 'db:create';
public const DOCTRINE_HELPER_SCRIPT = 'vendor/doctrine/orm/bin/doctrine.php';
public const DOCTRINE_HELPER_COMMAND = 'orm:schema-tool:create';
public const DOCTRINE_SCRIPT = 'vendor/doctrine/orm/bin/doctrine.php';
public const DOCTRINE_CREATE_SCHEMA_COMMAND = 'orm:schema-tool:create';
/** @var Connection */
private $regularConn;
@@ -60,7 +60,7 @@ class CreateDatabaseCommand extends AbstractDatabaseCommand
// Create database
$io->writeln('<fg=blue>Creating database tables...</>');
$this->runPhpCommand($output, [self::DOCTRINE_HELPER_SCRIPT, self::DOCTRINE_HELPER_COMMAND]);
$this->runPhpCommand($output, [self::DOCTRINE_SCRIPT, self::DOCTRINE_CREATE_SCHEMA_COMMAND]);
$io->success('Database properly created!');
return ExitCodes::EXIT_SUCCESS;
@@ -86,13 +86,8 @@ class CreateDatabaseCommand extends AbstractDatabaseCommand
private function schemaExists(): bool
{
// If at least one of the shlink tables exist, we will consider the database exists somehow.
// Any inconsistency will be taken care by the migrations
// Any inconsistency should be taken care by the migrations
$schemaManager = $this->regularConn->getSchemaManager();
return ! empty($schemaManager->listTableNames());
}
protected function getLockConfig(): LockedCommandConfig
{
return new LockedCommandConfig($this->getName(), true);
}
}

View File

@@ -1,9 +1,9 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Db;
use Shlinkio\Shlink\CLI\Command\Util\LockedCommandConfig;
use Shlinkio\Shlink\CLI\Util\ExitCodes;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@@ -12,8 +12,8 @@ use Symfony\Component\Console\Style\SymfonyStyle;
class MigrateDatabaseCommand extends AbstractDatabaseCommand
{
public const NAME = 'db:migrate';
public const DOCTRINE_HELPER_SCRIPT = 'vendor/doctrine/migrations/bin/doctrine-migrations.php';
public const DOCTRINE_HELPER_COMMAND = 'migrations:migrate';
public const DOCTRINE_MIGRATIONS_SCRIPT = 'vendor/doctrine/migrations/bin/doctrine-migrations.php';
public const DOCTRINE_MIGRATE_COMMAND = 'migrations:migrate';
protected function configure(): void
{
@@ -27,14 +27,9 @@ class MigrateDatabaseCommand extends AbstractDatabaseCommand
$io = new SymfonyStyle($input, $output);
$io->writeln('<fg=blue>Migrating database...</>');
$this->runPhpCommand($output, [self::DOCTRINE_HELPER_SCRIPT, self::DOCTRINE_HELPER_COMMAND]);
$this->runPhpCommand($output, [self::DOCTRINE_MIGRATIONS_SCRIPT, self::DOCTRINE_MIGRATE_COMMAND]);
$io->success('Database properly migrated!');
return ExitCodes::EXIT_SUCCESS;
}
protected function getLockConfig(): LockedCommandConfig
{
return new LockedCommandConfig($this->getName(), true);
}
}

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\ShortUrl;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\ShortUrl;

View File

@@ -1,9 +1,9 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\ShortUrl;
use Cake\Chronos\Chronos;
use Shlinkio\Shlink\CLI\Util\ExitCodes;
use Shlinkio\Shlink\Core\Exception\InvalidUrlException;
use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException;
@@ -101,7 +101,7 @@ class GenerateShortUrlCommand extends Command
return;
}
$longUrl = $io->ask('A long URL was not provided. Which URL do you want to be shortened?');
$longUrl = $io->ask('Which URL do you want to shorten?');
if (! empty($longUrl)) {
$input->setArgument('longUrl', $longUrl);
}
@@ -126,8 +126,8 @@ class GenerateShortUrlCommand extends Command
new Uri($longUrl),
$tags,
ShortUrlMeta::createFromParams(
$this->getOptionalDate($input, 'validSince'),
$this->getOptionalDate($input, 'validUntil'),
$input->getOption('validSince'),
$input->getOption('validUntil'),
$customSlug,
$maxVisits !== null ? (int) $maxVisits : null,
$input->getOption('findIfExists'),
@@ -150,10 +150,4 @@ class GenerateShortUrlCommand extends Command
return ExitCodes::EXIT_FAILURE;
}
}
private function getOptionalDate(InputInterface $input, string $fieldName): ?Chronos
{
$since = $input->getOption($fieldName);
return $since !== null ? Chronos::parse($since) : null;
}
}

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\ShortUrl;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\ShortUrl;
@@ -61,26 +62,26 @@ class ListShortUrlsCommand extends Command
->addOption(
'page',
'p',
InputOption::VALUE_OPTIONAL,
InputOption::VALUE_REQUIRED,
sprintf('The first page to list (%s items per page)', ShortUrlRepositoryAdapter::ITEMS_PER_PAGE),
'1'
)
->addOption(
'searchTerm',
's',
InputOption::VALUE_OPTIONAL,
InputOption::VALUE_REQUIRED,
'A query used to filter results by searching for it on the longUrl and shortCode fields'
)
->addOption(
'tags',
't',
InputOption::VALUE_OPTIONAL,
InputOption::VALUE_REQUIRED,
'A comma-separated list of tags to filter results'
)
->addOption(
'orderBy',
'o',
InputOption::VALUE_OPTIONAL,
InputOption::VALUE_REQUIRED,
'The field from which we want to order by. Pass ASC or DESC separated by a comma'
)
->addOption('showTags', null, InputOption::VALUE_NONE, 'Whether to display the tags or not');

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\ShortUrl;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Tag;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Tag;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Tag;

View File

@@ -1,10 +1,12 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Tag;
use Shlinkio\Shlink\CLI\Util\ExitCodes;
use Shlinkio\Shlink\Core\Exception\EntityDoesNotExistException;
use Shlinkio\Shlink\Core\Exception\TagConflictException;
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
@@ -49,6 +51,11 @@ class RenameTagCommand extends Command
} catch (EntityDoesNotExistException $e) {
$io->error(sprintf('A tag with name "%s" was not found', $oldName));
return ExitCodes::EXIT_FAILURE;
} catch (TagConflictException $e) {
$io->error(
sprintf('A tag with name "%s" cannot be renamed to "%s" because it already exists', $oldName, $newName)
);
return ExitCodes::EXIT_FAILURE;
}
}
}

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Util;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Util;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Visit;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Visit;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Exception;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Exception;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Factory;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Util;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Util;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Util;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Util;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace ShlinkioTest\Shlink\CLI\Command\Api;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace ShlinkioTest\Shlink\CLI\Command\Api;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace ShlinkioTest\Shlink\CLI\Command\Api;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace ShlinkioTest\Shlink\CLI\Command\Config;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace ShlinkioTest\Shlink\CLI\Command\Db;
@@ -115,10 +116,10 @@ class CreateDatabaseCommandTest extends TestCase
$createDatabase = $this->schemaManager->createDatabase($shlinkDatabase)->will(function () {
});
$listTables = $this->schemaManager->listTableNames()->willReturn([]);
$runCommand = $this->processHelper->run(Argument::type(OutputInterface::class), [
$runCommand = $this->processHelper->mustRun(Argument::type(OutputInterface::class), [
'/usr/local/bin/php',
CreateDatabaseCommand::DOCTRINE_HELPER_SCRIPT,
CreateDatabaseCommand::DOCTRINE_HELPER_COMMAND,
CreateDatabaseCommand::DOCTRINE_SCRIPT,
CreateDatabaseCommand::DOCTRINE_CREATE_SCHEMA_COMMAND,
], Argument::cetera());
$this->commandTester->execute([]);

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace ShlinkioTest\Shlink\CLI\Command\Db;
@@ -47,36 +48,20 @@ class MigrateDatabaseCommandTest extends TestCase
$this->commandTester = new CommandTester($command);
}
/**
* @test
* @dataProvider provideVerbosities
*/
public function migrationsCommandIsRunWithProperVerbosity(int $verbosity): void
/** @test */
public function migrationsCommandIsRunWithProperVerbosity(): void
{
$runCommand = $this->processHelper->run(Argument::type(OutputInterface::class), [
$runCommand = $this->processHelper->mustRun(Argument::type(OutputInterface::class), [
'/usr/local/bin/php',
MigrateDatabaseCommand::DOCTRINE_HELPER_SCRIPT,
MigrateDatabaseCommand::DOCTRINE_HELPER_COMMAND,
], null, null, $verbosity);
MigrateDatabaseCommand::DOCTRINE_MIGRATIONS_SCRIPT,
MigrateDatabaseCommand::DOCTRINE_MIGRATE_COMMAND,
], Argument::cetera());
$this->commandTester->execute([], [
'verbosity' => $verbosity,
]);
$this->commandTester->execute([]);
$output = $this->commandTester->getDisplay();
if ($verbosity >= OutputInterface::VERBOSITY_VERBOSE) {
$this->assertStringContainsString('Migrating database...', $output);
$this->assertStringContainsString('Database properly migrated!', $output);
}
$this->assertStringContainsString('Migrating database...', $output);
$this->assertStringContainsString('Database properly migrated!', $output);
$runCommand->shouldHaveBeenCalledOnce();
}
public function provideVerbosities(): iterable
{
yield 'debug' => [OutputInterface::VERBOSITY_DEBUG];
yield 'normal' => [OutputInterface::VERBOSITY_NORMAL];
yield 'quiet' => [OutputInterface::VERBOSITY_QUIET];
yield 'verbose' => [OutputInterface::VERBOSITY_VERBOSE];
yield 'very verbose' => [OutputInterface::VERBOSITY_VERY_VERBOSE];
}
}

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace ShlinkioTest\Shlink\CLI\Command\ShortUrl;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace ShlinkioTest\Shlink\CLI\Command\ShortUrl;

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace ShlinkioTest\Shlink\CLI\Command\ShortUrl;
@@ -19,6 +20,11 @@ use Symfony\Component\Console\Tester\CommandTester;
class GenerateShortUrlCommandTest extends TestCase
{
private const DOMAIN_CONFIG = [
'schema' => 'http',
'hostname' => 'foo.com',
];
/** @var CommandTester */
private $commandTester;
/** @var ObjectProphecy */
@@ -27,10 +33,7 @@ class GenerateShortUrlCommandTest extends TestCase
public function setUp(): void
{
$this->urlShortener = $this->prophesize(UrlShortener::class);
$command = new GenerateShortUrlCommand($this->urlShortener->reveal(), [
'schema' => 'http',
'hostname' => 'foo.com',
]);
$command = new GenerateShortUrlCommand($this->urlShortener->reveal(), self::DOMAIN_CONFIG);
$app = new Application();
$app->add($command);
$this->commandTester = new CommandTester($command);
@@ -39,9 +42,8 @@ class GenerateShortUrlCommandTest extends TestCase
/** @test */
public function properShortCodeIsCreatedIfLongUrlIsCorrect(): void
{
$urlToShortCode = $this->urlShortener->urlToShortCode(Argument::cetera())->willReturn(
(new ShortUrl(''))->setShortCode('abc123')
);
$shortUrl = new ShortUrl('');
$urlToShortCode = $this->urlShortener->urlToShortCode(Argument::cetera())->willReturn($shortUrl);
$this->commandTester->execute([
'longUrl' => 'http://domain.com/foo/bar',
@@ -50,7 +52,7 @@ class GenerateShortUrlCommandTest extends TestCase
$output = $this->commandTester->getDisplay();
$this->assertEquals(ExitCodes::EXIT_SUCCESS, $this->commandTester->getStatusCode());
$this->assertStringContainsString('http://foo.com/abc123', $output);
$this->assertStringContainsString($shortUrl->toString(self::DOMAIN_CONFIG), $output);
$urlToShortCode->shouldHaveBeenCalledOnce();
}
@@ -85,6 +87,7 @@ class GenerateShortUrlCommandTest extends TestCase
/** @test */
public function properlyProcessesProvidedTags(): void
{
$shortUrl = new ShortUrl('');
$urlToShortCode = $this->urlShortener->urlToShortCode(
Argument::type(UriInterface::class),
Argument::that(function (array $tags) {
@@ -92,7 +95,7 @@ class GenerateShortUrlCommandTest extends TestCase
return $tags;
}),
Argument::cetera()
)->willReturn((new ShortUrl(''))->setShortCode('abc123'));
)->willReturn($shortUrl);
$this->commandTester->execute([
'longUrl' => 'http://domain.com/foo/bar',
@@ -101,7 +104,7 @@ class GenerateShortUrlCommandTest extends TestCase
$output = $this->commandTester->getDisplay();
$this->assertEquals(ExitCodes::EXIT_SUCCESS, $this->commandTester->getStatusCode());
$this->assertStringContainsString('http://foo.com/abc123', $output);
$this->assertStringContainsString($shortUrl->toString(self::DOMAIN_CONFIG), $output);
$urlToShortCode->shouldHaveBeenCalledOnce();
}
}

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace ShlinkioTest\Shlink\CLI\Command\ShortUrl;

Some files were not shown because too many files have changed in this diff Show More