mirror of
https://github.com/shlinkio/shlink.git
synced 2025-03-29 13:03:52 +03:00
commit
2e10ee66b7
26 changed files with 323 additions and 99 deletions
113
CHANGELOG.md
113
CHANGELOG.md
|
@ -1,20 +1,35 @@
|
||||||
## CHANGELOG
|
## CHANGELOG
|
||||||
|
|
||||||
|
### 1.3.1
|
||||||
|
|
||||||
|
**Tasks**
|
||||||
|
|
||||||
|
* [82: Enable FastRoute routes cache](https://github.com/shlinkio/shlink/issues/82)
|
||||||
|
* [85: Update year in license file](https://github.com/shlinkio/shlink/issues/85)
|
||||||
|
* [81: Add docker containers config](https://github.com/shlinkio/shlink/issues/81)
|
||||||
|
|
||||||
|
**Bugs**
|
||||||
|
|
||||||
|
* [83: Short codes list: search in tags when filtering by query string](https://github.com/shlinkio/shlink/issues/83)
|
||||||
|
* [79: Increase the number of followed redirects](https://github.com/shlinkio/shlink/issues/79)
|
||||||
|
* [75: Apply PathVersionMiddleware only to rest routes defining it by configuration instead of code](https://github.com/shlinkio/shlink/issues/75)
|
||||||
|
* [77: Allow defining database server hostname and port](https://github.com/shlinkio/shlink/issues/77)
|
||||||
|
|
||||||
### 1.3.0
|
### 1.3.0
|
||||||
|
|
||||||
**Enhancements:**
|
**Enhancements:**
|
||||||
|
|
||||||
* [67: Allow to order the short codes list](https://github.com/acelaya/url-shortener/issues/67)
|
* [67: Allow to order the short codes list](https://github.com/shlinkio/shlink/issues/67)
|
||||||
* [60: Accept JSON requests in REST and use a body parser middleware to set the parsedBody](https://github.com/acelaya/url-shortener/issues/60)
|
* [60: Accept JSON requests in REST and use a body parser middleware to set the parsedBody](https://github.com/shlinkio/shlink/issues/60)
|
||||||
* [72: When listing API keys from CLI, display in yellow color enabled keys that have expired](https://github.com/acelaya/url-shortener/issues/72)
|
* [72: When listing API keys from CLI, display in yellow color enabled keys that have expired](https://github.com/shlinkio/shlink/issues/72)
|
||||||
* [58: Allow to filter short URLs by tag](https://github.com/acelaya/url-shortener/issues/58)
|
* [58: Allow to filter short URLs by tag](https://github.com/shlinkio/shlink/issues/58)
|
||||||
* [69: Allow to filter short codes by text query](https://github.com/acelaya/url-shortener/issues/69)
|
* [69: Allow to filter short codes by text query](https://github.com/shlinkio/shlink/issues/69)
|
||||||
|
|
||||||
**Tasks**
|
**Tasks**
|
||||||
|
|
||||||
* [73: Tag endpoints in swagger file](https://github.com/acelaya/url-shortener/issues/73)
|
* [73: Tag endpoints in swagger file](https://github.com/shlinkio/shlink/issues/73)
|
||||||
* [71: Separate swagger docs into multiple files](https://github.com/acelaya/url-shortener/issues/71)
|
* [71: Separate swagger docs into multiple files](https://github.com/shlinkio/shlink/issues/71)
|
||||||
* [63: Add path versioning to REST API routes](https://github.com/acelaya/url-shortener/issues/63)
|
* [63: Add path versioning to REST API routes](https://github.com/shlinkio/shlink/issues/63)
|
||||||
|
|
||||||
### 1.2.2
|
### 1.2.2
|
||||||
|
|
||||||
|
@ -26,91 +41,91 @@
|
||||||
|
|
||||||
**Bugs**
|
**Bugs**
|
||||||
|
|
||||||
* [62: Fix cross-domain requests in REST API](https://github.com/acelaya/url-shortener/issues/62)
|
* [62: Fix cross-domain requests in REST API](https://github.com/shlinkio/shlink/issues/62)
|
||||||
|
|
||||||
### 1.2.0
|
### 1.2.0
|
||||||
|
|
||||||
**Features**
|
**Features**
|
||||||
|
|
||||||
* [45: Allow to define tags on short codes, to improve filtering and classification](https://github.com/acelaya/url-shortener/issues/45)
|
* [45: Allow to define tags on short codes, to improve filtering and classification](https://github.com/shlinkio/shlink/issues/45)
|
||||||
* [7: Add website previews while listing available URLs](https://github.com/acelaya/url-shortener/issues/7)
|
* [7: Add website previews while listing available URLs](https://github.com/shlinkio/shlink/issues/7)
|
||||||
|
|
||||||
**Enhancements:**
|
**Enhancements:**
|
||||||
|
|
||||||
* [57: Add database migrations system to improve updating between versions](https://github.com/acelaya/url-shortener/issues/57)
|
* [57: Add database migrations system to improve updating between versions](https://github.com/shlinkio/shlink/issues/57)
|
||||||
* [31: Add support for other database management systems by improving the EntityManager factory](https://github.com/acelaya/url-shortener/issues/31)
|
* [31: Add support for other database management systems by improving the EntityManager factory](https://github.com/shlinkio/shlink/issues/31)
|
||||||
* [51: Generate build process to paquetize the app and ease distribution](https://github.com/acelaya/url-shortener/issues/51)
|
* [51: Generate build process to paquetize the app and ease distribution](https://github.com/shlinkio/shlink/issues/51)
|
||||||
* [38: Define installation script. It will request dynamic data on the fly so that there is no need to define env vars](https://github.com/acelaya/url-shortener/issues/38)
|
* [38: Define installation script. It will request dynamic data on the fly so that there is no need to define env vars](https://github.com/shlinkio/shlink/issues/38)
|
||||||
|
|
||||||
**Tasks**
|
**Tasks**
|
||||||
|
|
||||||
* [55: Create update script which does not try to create a new database](https://github.com/acelaya/url-shortener/issues/55)
|
* [55: Create update script which does not try to create a new database](https://github.com/shlinkio/shlink/issues/55)
|
||||||
* [54: Add cache namespace to prevent name collisions with other apps in the same environment](https://github.com/acelaya/url-shortener/issues/54)
|
* [54: Add cache namespace to prevent name collisions with other apps in the same environment](https://github.com/shlinkio/shlink/issues/54)
|
||||||
* [29: Use the acelaya/ze-content-based-error-handler package instead of custom error handler implementation](https://github.com/acelaya/url-shortener/issues/29)
|
* [29: Use the acelaya/ze-content-based-error-handler package instead of custom error handler implementation](https://github.com/shlinkio/shlink/issues/29)
|
||||||
|
|
||||||
**Bugs**
|
**Bugs**
|
||||||
|
|
||||||
* [53: Fix entities database interoperability](https://github.com/acelaya/url-shortener/issues/53)
|
* [53: Fix entities database interoperability](https://github.com/shlinkio/shlink/issues/53)
|
||||||
* [52: Add missing htaccess file for apache environments](https://github.com/acelaya/url-shortener/issues/52)
|
* [52: Add missing htaccess file for apache environments](https://github.com/shlinkio/shlink/issues/52)
|
||||||
|
|
||||||
### 1.1.0
|
### 1.1.0
|
||||||
|
|
||||||
**Features**
|
**Features**
|
||||||
|
|
||||||
* [46: Define a route that returns a QR code representing the shortened URL](https://github.com/acelaya/url-shortener/issues/46)
|
* [46: Define a route that returns a QR code representing the shortened URL](https://github.com/shlinkio/shlink/issues/46)
|
||||||
|
|
||||||
**Enhancements:**
|
**Enhancements:**
|
||||||
|
|
||||||
* [32: Add support for other cache adapters by improving the Cache factory](https://github.com/acelaya/url-shortener/issues/32)
|
* [32: Add support for other cache adapters by improving the Cache factory](https://github.com/shlinkio/shlink/issues/32)
|
||||||
* [14: https://github.com/shlinkio/shlink/issues/14](https://github.com/acelaya/url-shortener/issues/14)
|
* [14: https://github.com/shlinkio/shlink/issues/14](https://github.com/shlinkio/shlink/issues/14)
|
||||||
* [41: Cache the "short code" => "URL" map to prevent extra DB hits](https://github.com/acelaya/url-shortener/issues/41)
|
* [41: Cache the "short code" => "URL" map to prevent extra DB hits](https://github.com/shlinkio/shlink/issues/41)
|
||||||
* [13: Improve REST authentication](https://github.com/acelaya/url-shortener/issues/13)
|
* [13: Improve REST authentication](https://github.com/shlinkio/shlink/issues/13)
|
||||||
|
|
||||||
**Tasks**
|
**Tasks**
|
||||||
|
|
||||||
* [39: Change copyright from "Alejandro Celaya" to "Shlink" in error pages](https://github.com/acelaya/url-shortener/issues/39)
|
* [39: Change copyright from "Alejandro Celaya" to "Shlink" in error pages](https://github.com/shlinkio/shlink/issues/39)
|
||||||
* [42: Make REST endpoints that need to find something return a 404 when "something" is not found](https://github.com/acelaya/url-shortener/issues/42)
|
* [42: Make REST endpoints that need to find something return a 404 when "something" is not found](https://github.com/shlinkio/shlink/issues/42)
|
||||||
* [35: Make CLI commands to use the same PHP namespace as the one used for the command name](https://github.com/acelaya/url-shortener/issues/35)
|
* [35: Make CLI commands to use the same PHP namespace as the one used for the command name](https://github.com/shlinkio/shlink/issues/35)
|
||||||
|
|
||||||
**Bugs**
|
**Bugs**
|
||||||
|
|
||||||
* [40: Take into account the X-Forwarded-For header in order to get the visitor information, in case the server is behind a load balancer or proxy](https://github.com/acelaya/url-shortener/issues/40)
|
* [40: Take into account the X-Forwarded-For header in order to get the visitor information, in case the server is behind a load balancer or proxy](https://github.com/shlinkio/shlink/issues/40)
|
||||||
|
|
||||||
### 1.0.0
|
### 1.0.0
|
||||||
|
|
||||||
**Enhancements:**
|
**Enhancements:**
|
||||||
|
|
||||||
* [33: Create a command to generate a short code charset by randomizing the default one](https://github.com/acelaya/url-shortener/issues/33)
|
* [33: Create a command to generate a short code charset by randomizing the default one](https://github.com/shlinkio/shlink/issues/33)
|
||||||
* [15: Return JSON/HTML responses for errors (4xx and 5xx) based on accept header (content negotiation)](https://github.com/acelaya/url-shortener/issues/15)
|
* [15: Return JSON/HTML responses for errors (4xx and 5xx) based on accept header (content negotiation)](https://github.com/shlinkio/shlink/issues/15)
|
||||||
* [23: Translate application literals](https://github.com/acelaya/url-shortener/issues/23)
|
* [23: Translate application literals](https://github.com/shlinkio/shlink/issues/23)
|
||||||
* [21: Allow to filter visits by date range](https://github.com/acelaya/url-shortener/issues/21)
|
* [21: Allow to filter visits by date range](https://github.com/shlinkio/shlink/issues/21)
|
||||||
* [22: Save visits locations data on a visit_locations table](https://github.com/acelaya/url-shortener/issues/22)
|
* [22: Save visits locations data on a visit_locations table](https://github.com/shlinkio/shlink/issues/22)
|
||||||
* [20: Inject cross domain headers in response only if the Origin header is present in the request](https://github.com/acelaya/url-shortener/issues/20)
|
* [20: Inject cross domain headers in response only if the Origin header is present in the request](https://github.com/shlinkio/shlink/issues/20)
|
||||||
* [11: Separate code into multiple modules](https://github.com/acelaya/url-shortener/issues/11)
|
* [11: Separate code into multiple modules](https://github.com/shlinkio/shlink/issues/11)
|
||||||
* [18: Group routable middleware in an Action namespace](https://github.com/acelaya/url-shortener/issues/18)
|
* [18: Group routable middleware in an Action namespace](https://github.com/shlinkio/shlink/issues/18)
|
||||||
|
|
||||||
**Tasks**
|
**Tasks**
|
||||||
|
|
||||||
* [36: Remove hhvm from the CI matrix since it doesn't support array constants and will fail](https://github.com/acelaya/url-shortener/issues/36)
|
* [36: Remove hhvm from the CI matrix since it doesn't support array constants and will fail](https://github.com/shlinkio/shlink/issues/36)
|
||||||
* [4: Installation steps](https://github.com/acelaya/url-shortener/issues/4)
|
* [4: Installation steps](https://github.com/shlinkio/shlink/issues/4)
|
||||||
* [6: Remove dependency on expressive helpers package](https://github.com/acelaya/url-shortener/issues/6)
|
* [6: Remove dependency on expressive helpers package](https://github.com/shlinkio/shlink/issues/6)
|
||||||
* [30: Replace the "services" first level config entry by "dependencies", in order to fulfill default Expressive name](https://github.com/acelaya/url-shortener/issues/30)
|
* [30: Replace the "services" first level config entry by "dependencies", in order to fulfill default Expressive name](https://github.com/shlinkio/shlink/issues/30)
|
||||||
* [12: Improve code coverage](https://github.com/acelaya/url-shortener/issues/12)
|
* [12: Improve code coverage](https://github.com/shlinkio/shlink/issues/12)
|
||||||
* [25: Replace "Middleware" suffix on routable middlewares by "Action"](https://github.com/acelaya/url-shortener/issues/25)
|
* [25: Replace "Middleware" suffix on routable middlewares by "Action"](https://github.com/shlinkio/shlink/issues/25)
|
||||||
* [19: Update the vendor and app namespace from Acelaya\UrlShortener to Shlinkio\Shlink](https://github.com/acelaya/url-shortener/issues/19)
|
* [19: Update the vendor and app namespace from Acelaya\UrlShortener to Shlinkio\Shlink](https://github.com/shlinkio/shlink/issues/19)
|
||||||
|
|
||||||
**Bugs**
|
**Bugs**
|
||||||
|
|
||||||
* [24: Prevent duplicated shortcodes errors because of the case insensitive behavior on MySQL](https://github.com/acelaya/url-shortener/issues/24)
|
* [24: Prevent duplicated shortcodes errors because of the case insensitive behavior on MySQL](https://github.com/shlinkio/shlink/issues/24)
|
||||||
|
|
||||||
### 0.2.0
|
### 0.2.0
|
||||||
|
|
||||||
**Enhancements:**
|
**Enhancements:**
|
||||||
|
|
||||||
* [9: Use symfony/console to dispatch console requests, instead of trying to integrate the process with expressive](https://github.com/acelaya/url-shortener/issues/9)
|
* [9: Use symfony/console to dispatch console requests, instead of trying to integrate the process with expressive](https://github.com/shlinkio/shlink/issues/9)
|
||||||
* [8: Create a REST API](https://github.com/acelaya/url-shortener/issues/8)
|
* [8: Create a REST API](https://github.com/shlinkio/shlink/issues/8)
|
||||||
* [10: Add more CLI functionality](https://github.com/acelaya/url-shortener/issues/10)
|
* [10: Add more CLI functionality](https://github.com/shlinkio/shlink/issues/10)
|
||||||
|
|
||||||
**Tasks**
|
**Tasks**
|
||||||
|
|
||||||
* [5: Create CHANGELOG file](https://github.com/acelaya/url-shortener/issues/5)
|
* [5: Create CHANGELOG file](https://github.com/shlinkio/shlink/issues/5)
|
||||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2016 Alejandro Celaya
|
Copyright (c) 2017 Alejandro Celaya
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
4
build.sh
4
build.sh
|
@ -15,6 +15,7 @@ projectdir=$(pwd)
|
||||||
echo 'Copying project files...'
|
echo 'Copying project files...'
|
||||||
rm -rf "${builtcontent}"
|
rm -rf "${builtcontent}"
|
||||||
mkdir "${builtcontent}"
|
mkdir "${builtcontent}"
|
||||||
|
sudo chmod -R 777 "${projectdir}"/data/infra/{database,nginx}
|
||||||
cp -R "${projectdir}"/* "${builtcontent}"
|
cp -R "${projectdir}"/* "${builtcontent}"
|
||||||
cd "${builtcontent}"
|
cd "${builtcontent}"
|
||||||
|
|
||||||
|
@ -22,7 +23,7 @@ cd "${builtcontent}"
|
||||||
rm -r vendor
|
rm -r vendor
|
||||||
rm composer.lock
|
rm composer.lock
|
||||||
composer self-update
|
composer self-update
|
||||||
composer install --no-dev --optimize-autoloader
|
composer install --no-dev --optimize-autoloader --no-progress --no-interaction
|
||||||
|
|
||||||
# Delete development files
|
# Delete development files
|
||||||
echo 'Deleting dev files...'
|
echo 'Deleting dev files...'
|
||||||
|
@ -34,6 +35,7 @@ rm php*
|
||||||
rm README.md
|
rm README.md
|
||||||
rm -r build
|
rm -r build
|
||||||
rm -f data/database.sqlite
|
rm -f data/database.sqlite
|
||||||
|
rm -rf data/infra
|
||||||
rm -rf data/{cache,log,proxies}/{*,.gitignore}
|
rm -rf data/{cache,log,proxies}/{*,.gitignore}
|
||||||
rm -rf config/params/{*,.gitignore}
|
rm -rf config/params/{*,.gitignore}
|
||||||
rm -rf config/autoload/{{,*.}local.php{,.dist},.gitignore}
|
rm -rf config/autoload/{{,*.}local.php{,.dist},.gitignore}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^5.6 || ^7.0",
|
"php": "^5.6 || ^7.0",
|
||||||
"zendframework/zend-expressive": "^1.0",
|
"zendframework/zend-expressive": "^1.0",
|
||||||
"zendframework/zend-expressive-fastroute": "^1.1",
|
"zendframework/zend-expressive-fastroute": "^1.3",
|
||||||
"zendframework/zend-expressive-twigrenderer": "^1.0",
|
"zendframework/zend-expressive-twigrenderer": "^1.0",
|
||||||
"zendframework/zend-stdlib": "^2.7",
|
"zendframework/zend-stdlib": "^2.7",
|
||||||
"zendframework/zend-servicemanager": "^3.0",
|
"zendframework/zend-servicemanager": "^3.0",
|
||||||
|
|
|
@ -4,18 +4,15 @@ use Zend\Expressive\Container;
|
||||||
use Zend\Expressive\Router;
|
use Zend\Expressive\Router;
|
||||||
use Zend\Expressive\Template;
|
use Zend\Expressive\Template;
|
||||||
use Zend\Expressive\Twig;
|
use Zend\Expressive\Twig;
|
||||||
use Zend\ServiceManager\Factory\InvokableFactory;
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
'dependencies' => [
|
'dependencies' => [
|
||||||
'factories' => [
|
'factories' => [
|
||||||
Expressive\Application::class => Container\ApplicationFactory::class,
|
Expressive\Application::class => Container\ApplicationFactory::class,
|
||||||
Router\FastRouteRouter::class => InvokableFactory::class,
|
|
||||||
Template\TemplateRendererInterface::class => Twig\TwigRendererFactory::class,
|
Template\TemplateRendererInterface::class => Twig\TwigRendererFactory::class,
|
||||||
],
|
\Twig_Environment::class => Twig\TwigEnvironmentFactory::class,
|
||||||
'aliases' => [
|
Router\RouterInterface::class => Router\FastRouteRouterFactory::class,
|
||||||
Router\RouterInterface::class => Router\FastRouteRouter::class,
|
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
|
@ -6,14 +6,10 @@ return [
|
||||||
'proxies_dir' => 'data/proxies',
|
'proxies_dir' => 'data/proxies',
|
||||||
],
|
],
|
||||||
'connection' => [
|
'connection' => [
|
||||||
'driver' => 'pdo_mysql',
|
|
||||||
'user' => env('DB_USER'),
|
'user' => env('DB_USER'),
|
||||||
'password' => env('DB_PASSWORD'),
|
'password' => env('DB_PASSWORD'),
|
||||||
'dbname' => env('DB_NAME', 'shlink'),
|
'dbname' => env('DB_NAME', 'shlink'),
|
||||||
'charset' => 'utf8',
|
'charset' => 'utf8',
|
||||||
'driverOptions' => [
|
|
||||||
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
14
config/autoload/entity-manager.local.php.dist
Normal file
14
config/autoload/entity-manager.local.php.dist
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
return [
|
||||||
|
|
||||||
|
'entity_manager' => [
|
||||||
|
'connection' => [
|
||||||
|
'driver' => 'pdo_mysql',
|
||||||
|
'host' => 'shlink_db',
|
||||||
|
'driverOptions' => [
|
||||||
|
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
13
config/autoload/router.global.php
Normal file
13
config/autoload/router.global.php
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
use Zend\Expressive\Router\FastRouteRouter;
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
'router' => [
|
||||||
|
'fastroute' => [
|
||||||
|
FastRouteRouter::CONFIG_CACHE_ENABLED => true,
|
||||||
|
FastRouteRouter::CONFIG_CACHE_FILE => 'data/cache/fastroute_cached_routes.php',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
12
config/autoload/router.local.php.dist
Normal file
12
config/autoload/router.local.php.dist
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
use Zend\Expressive\Router\FastRouteRouter;
|
||||||
|
|
||||||
|
return [
|
||||||
|
|
||||||
|
'router' => [
|
||||||
|
'fastroute' => [
|
||||||
|
FastRouteRouter::CONFIG_CACHE_ENABLED => false,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
];
|
2
data/infra/database/.gitignore
vendored
Normal file
2
data/infra/database/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
*
|
||||||
|
!.gitignore
|
6
data/infra/db.Dockerfile
Normal file
6
data/infra/db.Dockerfile
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
FROM mysql:5.7
|
||||||
|
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
|
||||||
|
|
||||||
|
# Enable remote access (default is localhost only, we change this
|
||||||
|
# otherwise our database would not be reachable from outside the container)
|
||||||
|
RUN sed -i -e"s/^bind-address\s*=\s*127.0.0.1/bind-address = 0.0.0.0/" /etc/mysql/my.cnf
|
5
data/infra/nginx.Dockerfile
Normal file
5
data/infra/nginx.Dockerfile
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
FROM nginx:1.11.6-alpine
|
||||||
|
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
|
||||||
|
|
||||||
|
# Delete default nginx vhost
|
||||||
|
RUN rm /etc/nginx/conf.d/default.conf
|
2
data/infra/nginx/.gitignore
vendored
Normal file
2
data/infra/nginx/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
*
|
||||||
|
!.gitignore
|
87
data/infra/php.Dockerfile
Normal file
87
data/infra/php.Dockerfile
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
FROM php:7.1-fpm-alpine
|
||||||
|
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
|
||||||
|
|
||||||
|
RUN apk update
|
||||||
|
|
||||||
|
# Install common php extensions
|
||||||
|
RUN docker-php-ext-install pdo_mysql
|
||||||
|
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 docker-php-ext-install pdo_sqlite
|
||||||
|
|
||||||
|
RUN apk add --no-cache --virtual icu-dev
|
||||||
|
RUN docker-php-ext-install intl
|
||||||
|
|
||||||
|
RUN apk add --no-cache --virtual zlib-dev
|
||||||
|
RUN docker-php-ext-install zip
|
||||||
|
|
||||||
|
RUN apk add --no-cache --virtual libmcrypt-dev
|
||||||
|
RUN docker-php-ext-install mcrypt
|
||||||
|
|
||||||
|
RUN apk add --no-cache --virtual libpng-dev
|
||||||
|
RUN docker-php-ext-install gd
|
||||||
|
|
||||||
|
# Install redis extension
|
||||||
|
ADD https://github.com/phpredis/phpredis/archive/php7.tar.gz /tmp/phpredis.tar.gz
|
||||||
|
RUN mkdir -p /usr/src/php/ext/redis\
|
||||||
|
&& tar xf /tmp/phpredis.tar.gz -C /usr/src/php/ext/redis --strip-components=1
|
||||||
|
# configure and install
|
||||||
|
RUN docker-php-ext-configure redis\
|
||||||
|
&& docker-php-ext-install redis
|
||||||
|
# cleanup
|
||||||
|
RUN rm /tmp/phpredis.tar.gz
|
||||||
|
|
||||||
|
# Install memcached extension
|
||||||
|
RUN apk add --no-cache --virtual cyrus-sasl-dev
|
||||||
|
RUN apk add --no-cache --virtual libmemcached-dev
|
||||||
|
ADD https://github.com/php-memcached-dev/php-memcached/archive/php7.tar.gz /tmp/memcached.tar.gz
|
||||||
|
RUN mkdir -p /usr/src/php/ext/memcached\
|
||||||
|
&& tar xf /tmp/memcached.tar.gz -C /usr/src/php/ext/memcached --strip-components=1
|
||||||
|
# configure and install
|
||||||
|
RUN docker-php-ext-configure memcached\
|
||||||
|
&& docker-php-ext-install memcached
|
||||||
|
# cleanup
|
||||||
|
RUN rm /tmp/memcached.tar.gz
|
||||||
|
|
||||||
|
# Install APCu extension
|
||||||
|
ADD https://pecl.php.net/get/apcu-5.1.3.tgz /tmp/apcu.tar.gz
|
||||||
|
RUN mkdir -p /usr/src/php/ext/apcu\
|
||||||
|
&& tar xf /tmp/apcu.tar.gz -C /usr/src/php/ext/apcu --strip-components=1
|
||||||
|
# configure and install
|
||||||
|
RUN docker-php-ext-configure apcu\
|
||||||
|
&& docker-php-ext-install apcu
|
||||||
|
# cleanup
|
||||||
|
RUN rm /tmp/apcu.tar.gz
|
||||||
|
|
||||||
|
# Install APCu-BC extension
|
||||||
|
ADD https://pecl.php.net/get/apcu_bc-1.0.3.tgz /tmp/apcu_bc.tar.gz
|
||||||
|
RUN mkdir -p /usr/src/php/ext/apcu-bc\
|
||||||
|
&& tar xf /tmp/apcu_bc.tar.gz -C /usr/src/php/ext/apcu-bc --strip-components=1
|
||||||
|
# configure and install
|
||||||
|
RUN docker-php-ext-configure apcu-bc\
|
||||||
|
&& docker-php-ext-install apcu-bc
|
||||||
|
# cleanup
|
||||||
|
RUN rm /tmp/apcu_bc.tar.gz
|
||||||
|
|
||||||
|
# Load APCU.ini before APC.ini
|
||||||
|
RUN rm /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini
|
||||||
|
RUN echo extension=apcu.so > /usr/local/etc/php/conf.d/20-php-ext-apcu.ini
|
||||||
|
|
||||||
|
# Install xdebug
|
||||||
|
ADD https://pecl.php.net/get/xdebug-2.5.0 /tmp/xdebug.tar.gz
|
||||||
|
RUN mkdir -p /usr/src/php/ext/xdebug\
|
||||||
|
&& tar xf /tmp/xdebug.tar.gz -C /usr/src/php/ext/xdebug --strip-components=1
|
||||||
|
# configure and install
|
||||||
|
RUN docker-php-ext-configure xdebug\
|
||||||
|
&& docker-php-ext-install xdebug
|
||||||
|
# cleanup
|
||||||
|
RUN rm /tmp/xdebug.tar.gz
|
||||||
|
|
||||||
|
# Install composer
|
||||||
|
RUN php -r "readfile('https://getcomposer.org/installer');" | php
|
||||||
|
RUN chmod +x composer.phar
|
||||||
|
RUN mv composer.phar /usr/local/bin/composer
|
1
data/infra/php.ini
Normal file
1
data/infra/php.ini
Normal file
|
@ -0,0 +1 @@
|
||||||
|
date.timezone = Europe/Madrid
|
21
data/infra/vhost.conf
Normal file
21
data/infra/vhost.conf
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
server {
|
||||||
|
listen 80 default_server;
|
||||||
|
server_name shlink.local;
|
||||||
|
root /home/shlink/www/public;
|
||||||
|
index index.php;
|
||||||
|
|
||||||
|
charset utf-8;
|
||||||
|
error_log /home/shlink/www/data/infra/nginx/shlink.error.log;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.php$is_args$args;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ \.php$ {
|
||||||
|
root /home/shlink/www/public;
|
||||||
|
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||||
|
fastcgi_pass shlink_php:9000;
|
||||||
|
fastcgi_index index.php;
|
||||||
|
include fastcgi.conf;
|
||||||
|
}
|
||||||
|
}
|
41
docker-compose.yml
Normal file
41
docker-compose.yml
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
version: '2'
|
||||||
|
|
||||||
|
services:
|
||||||
|
shlink_nginx:
|
||||||
|
container_name: shlink_nginx
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: ./data/infra/nginx.Dockerfile
|
||||||
|
ports:
|
||||||
|
- "8000:80"
|
||||||
|
volumes:
|
||||||
|
- ./:/home/shlink/www
|
||||||
|
- ./docs:/home/shlink/www/public/docs
|
||||||
|
- ./data/infra/vhost.conf:/etc/nginx/conf.d/shlink-vhost.conf
|
||||||
|
links:
|
||||||
|
- shlink_php
|
||||||
|
|
||||||
|
shlink_php:
|
||||||
|
container_name: shlink_php
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: ./data/infra/php.Dockerfile
|
||||||
|
volumes:
|
||||||
|
- ./:/home/shlink/www
|
||||||
|
- ./data/infra/php.ini:/usr/local/etc/php/php.ini
|
||||||
|
links:
|
||||||
|
- shlink_db
|
||||||
|
|
||||||
|
shlink_db:
|
||||||
|
container_name: shlink_db
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: ./data/infra/db.Dockerfile
|
||||||
|
ports:
|
||||||
|
- "3307:3306"
|
||||||
|
volumes:
|
||||||
|
- ./:/home/shlink/www
|
||||||
|
- ./data/infra/database:/var/lib/mysql
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: root
|
||||||
|
MYSQL_DATABASE: shlink
|
|
@ -26,10 +26,8 @@
|
||||||
"description": "A list of tags used to filter the resultset. Only short URLs tagged with at least one of the provided tags will be returned. (Since v1.3.0)",
|
"description": "A list of tags used to filter the resultset. Only short URLs tagged with at least one of the provided tags will be returned. (Since v1.3.0)",
|
||||||
"required": false,
|
"required": false,
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"schema": {
|
"items": {
|
||||||
"items": {
|
"type": "string"
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
2
indocker
Executable file
2
indocker
Executable file
|
@ -0,0 +1,2 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
docker exec -it shlink_php /bin/sh -c "cd /home/shlink/www && $*"
|
|
@ -135,11 +135,18 @@ class InstallCommand extends Command
|
||||||
$params['NAME'] = $this->ask('Database name', 'shlink');
|
$params['NAME'] = $this->ask('Database name', 'shlink');
|
||||||
$params['USER'] = $this->ask('Database username');
|
$params['USER'] = $this->ask('Database username');
|
||||||
$params['PASSWORD'] = $this->ask('Database password');
|
$params['PASSWORD'] = $this->ask('Database password');
|
||||||
|
$params['HOST'] = $this->ask('Database host', 'localhost');
|
||||||
|
$params['PORT'] = $this->ask('Database port', $this->getDefaultDbPort($params['DRIVER']));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $params;
|
return $params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getDefaultDbPort($driver)
|
||||||
|
{
|
||||||
|
return $driver === 'pdo_mysql' ? '3306' : '5432';
|
||||||
|
}
|
||||||
|
|
||||||
protected function askUrlShortener()
|
protected function askUrlShortener()
|
||||||
{
|
{
|
||||||
$this->printTitle('URL SHORTENER');
|
$this->printTitle('URL SHORTENER');
|
||||||
|
@ -272,6 +279,14 @@ class InstallCommand extends Command
|
||||||
$config['entity_manager']['connection']['user'] = $params['DATABASE']['USER'];
|
$config['entity_manager']['connection']['user'] = $params['DATABASE']['USER'];
|
||||||
$config['entity_manager']['connection']['password'] = $params['DATABASE']['PASSWORD'];
|
$config['entity_manager']['connection']['password'] = $params['DATABASE']['PASSWORD'];
|
||||||
$config['entity_manager']['connection']['dbname'] = $params['DATABASE']['NAME'];
|
$config['entity_manager']['connection']['dbname'] = $params['DATABASE']['NAME'];
|
||||||
|
$config['entity_manager']['connection']['host'] = $params['DATABASE']['HOST'];
|
||||||
|
$config['entity_manager']['connection']['port'] = $params['DATABASE']['PORT'];
|
||||||
|
|
||||||
|
if ($params['DATABASE']['DRIVER'] === 'pdo_mysql') {
|
||||||
|
$config['entity_manager']['connection']['driverOptions'] = [
|
||||||
|
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $config;
|
return $config;
|
||||||
|
|
|
@ -47,12 +47,14 @@ class InstallCommandTest extends TestCase
|
||||||
|
|
||||||
protected function createInputStream()
|
protected function createInputStream()
|
||||||
{
|
{
|
||||||
$stream = fopen('php://memory', 'r+', false);
|
$stream = fopen('php://memory', 'rb+', false);
|
||||||
fputs($stream, <<<CLI_INPUT
|
fwrite($stream, <<<CLI_INPUT
|
||||||
|
|
||||||
shlink_db
|
shlink_db
|
||||||
alejandro
|
alejandro
|
||||||
1234
|
1234
|
||||||
|
|
||||||
|
|
||||||
0
|
0
|
||||||
doma.in
|
doma.in
|
||||||
abc123BCA
|
abc123BCA
|
||||||
|
@ -69,7 +71,7 @@ CLI_INPUT
|
||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
*/
|
*/
|
||||||
public function testInputIsProperlyParsed()
|
public function inputIsProperlyParsed()
|
||||||
{
|
{
|
||||||
$this->configWriter->toFile(Argument::any(), [
|
$this->configWriter->toFile(Argument::any(), [
|
||||||
'app_options' => [
|
'app_options' => [
|
||||||
|
@ -81,6 +83,11 @@ CLI_INPUT
|
||||||
'dbname' => 'shlink_db',
|
'dbname' => 'shlink_db',
|
||||||
'user' => 'alejandro',
|
'user' => 'alejandro',
|
||||||
'password' => '1234',
|
'password' => '1234',
|
||||||
|
'host' => 'localhost',
|
||||||
|
'port' => '3306',
|
||||||
|
'driverOptions' => [
|
||||||
|
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
|
||||||
|
]
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'translator' => [
|
'translator' => [
|
||||||
|
|
|
@ -21,15 +21,15 @@ class ShortUrlRepository extends EntityRepository implements ShortUrlRepositoryI
|
||||||
$qb->select('s');
|
$qb->select('s');
|
||||||
|
|
||||||
// Set limit and offset
|
// Set limit and offset
|
||||||
if (isset($limit)) {
|
if ($limit !== null) {
|
||||||
$qb->setMaxResults($limit);
|
$qb->setMaxResults($limit);
|
||||||
}
|
}
|
||||||
if (isset($offset)) {
|
if ($offset !== null) {
|
||||||
$qb->setFirstResult($offset);
|
$qb->setFirstResult($offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
// In case the ordering has been specified, the query could be more complex. Process it
|
// In case the ordering has been specified, the query could be more complex. Process it
|
||||||
if (isset($orderBy)) {
|
if ($orderBy !== null) {
|
||||||
return $this->processOrderByForList($qb, $orderBy);
|
return $this->processOrderByForList($qb, $orderBy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ class ShortUrlRepository extends EntityRepository implements ShortUrlRepositoryI
|
||||||
'visits',
|
'visits',
|
||||||
'visitsCount',
|
'visitsCount',
|
||||||
'visitCount',
|
'visitCount',
|
||||||
])) {
|
], true)) {
|
||||||
$qb->addSelect('COUNT(v) AS totalVisits')
|
$qb->addSelect('COUNT(v) AS totalVisits')
|
||||||
->leftJoin('s.visits', 'v')
|
->leftJoin('s.visits', 'v')
|
||||||
->groupBy('s')
|
->groupBy('s')
|
||||||
|
@ -58,7 +58,7 @@ class ShortUrlRepository extends EntityRepository implements ShortUrlRepositoryI
|
||||||
'originalUrl',
|
'originalUrl',
|
||||||
'shortCode',
|
'shortCode',
|
||||||
'dateCreated',
|
'dateCreated',
|
||||||
])) {
|
], true)) {
|
||||||
$qb->orderBy('s.' . $fieldName, $order);
|
$qb->orderBy('s.' . $fieldName, $order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,9 +93,12 @@ class ShortUrlRepository extends EntityRepository implements ShortUrlRepositoryI
|
||||||
|
|
||||||
// Apply search term to every searchable field if not empty
|
// Apply search term to every searchable field if not empty
|
||||||
if (! empty($searchTerm)) {
|
if (! empty($searchTerm)) {
|
||||||
|
$qb->join('s.tags', 't');
|
||||||
|
|
||||||
$conditions = [
|
$conditions = [
|
||||||
$qb->expr()->like('s.originalUrl', ':searchPattern'),
|
$qb->expr()->like('s.originalUrl', ':searchPattern'),
|
||||||
$qb->expr()->like('s.shortCode', ':searchPattern'),
|
$qb->expr()->like('s.shortCode', ':searchPattern'),
|
||||||
|
$qb->expr()->like('t.name', ':searchPattern'),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Unpack and apply search conditions
|
// Unpack and apply search conditions
|
||||||
|
|
|
@ -117,7 +117,9 @@ class UrlShortener implements UrlShortenerInterface
|
||||||
protected function checkUrlExists(UriInterface $url)
|
protected function checkUrlExists(UriInterface $url)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$this->httpClient->request('GET', $url);
|
$this->httpClient->request('GET', $url, ['allow_redirects' => [
|
||||||
|
'max' => 15,
|
||||||
|
]]);
|
||||||
} catch (GuzzleException $e) {
|
} catch (GuzzleException $e) {
|
||||||
throw InvalidUrlException::fromUrl($url, $e);
|
throw InvalidUrlException::fromUrl($url, $e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ return [
|
||||||
|
|
||||||
'middleware_pipeline' => [
|
'middleware_pipeline' => [
|
||||||
'pre-routing' => [
|
'pre-routing' => [
|
||||||
|
'path' => '/rest',
|
||||||
'middleware' => [
|
'middleware' => [
|
||||||
Middleware\PathVersionMiddleware::class,
|
Middleware\PathVersionMiddleware::class,
|
||||||
],
|
],
|
||||||
|
|
|
@ -37,19 +37,13 @@ class PathVersionMiddleware implements MiddlewareInterface
|
||||||
$uri = $request->getUri();
|
$uri = $request->getUri();
|
||||||
$path = $uri->getPath();
|
$path = $uri->getPath();
|
||||||
|
|
||||||
// Exclude non-rest route
|
|
||||||
if (strpos($path, '/rest') !== 0) {
|
|
||||||
return $out($request, $response);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the path does not begin with the version number, prepend v1 by default for retrocompatibility purposes
|
// If the path does not begin with the version number, prepend v1 by default for retrocompatibility purposes
|
||||||
if (strpos($path, '/rest/v') !== 0) {
|
if (strpos($path, '/v') !== 0) {
|
||||||
$parts = explode('/', $path);
|
$parts = explode('/', $path);
|
||||||
// Remove the first empty part and the "/rest" prefix
|
// Remove the first empty part and the
|
||||||
array_shift($parts);
|
array_shift($parts);
|
||||||
array_shift($parts);
|
// Prepend the version prefix
|
||||||
// Prepend the prefix with version
|
array_unshift($parts, '/v1');
|
||||||
array_unshift($parts, '/rest/v1');
|
|
||||||
|
|
||||||
$request = $request->withUri($uri->withPath(implode('/', $parts)));
|
$request = $request->withUri($uri->withPath(implode('/', $parts)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ class PathVersionMiddlewareTest extends TestCase
|
||||||
*/
|
*/
|
||||||
public function whenVersionIsProvidedRequestRemainsUnchanged()
|
public function whenVersionIsProvidedRequestRemainsUnchanged()
|
||||||
{
|
{
|
||||||
$request = ServerRequestFactory::fromGlobals()->withUri(new Uri('/rest/v2/foo'));
|
$request = ServerRequestFactory::fromGlobals()->withUri(new Uri('/v2/foo'));
|
||||||
$test = $this;
|
$test = $this;
|
||||||
$this->middleware->__invoke($request, new Response(), function ($req) use ($request, $test) {
|
$this->middleware->__invoke($request, new Response(), function ($req) use ($request, $test) {
|
||||||
$test->assertSame($request, $req);
|
$test->assertSame($request, $req);
|
||||||
|
@ -37,23 +37,11 @@ class PathVersionMiddlewareTest extends TestCase
|
||||||
*/
|
*/
|
||||||
public function versionOneIsPrependedWhenNoVersionIsDefined()
|
public function versionOneIsPrependedWhenNoVersionIsDefined()
|
||||||
{
|
{
|
||||||
$request = ServerRequestFactory::fromGlobals()->withUri(new Uri('/rest/bar/baz'));
|
$request = ServerRequestFactory::fromGlobals()->withUri(new Uri('/bar/baz'));
|
||||||
$test = $this;
|
$test = $this;
|
||||||
$this->middleware->__invoke($request, new Response(), function (Request $req) use ($request, $test) {
|
$this->middleware->__invoke($request, new Response(), function (Request $req) use ($request, $test) {
|
||||||
$test->assertNotSame($request, $req);
|
$test->assertNotSame($request, $req);
|
||||||
$this->assertEquals('/rest/v1/bar/baz', $req->getUri()->getPath());
|
$this->assertEquals('/v1/bar/baz', $req->getUri()->getPath());
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @test
|
|
||||||
*/
|
|
||||||
public function nonRestPathsAreNotProcessed()
|
|
||||||
{
|
|
||||||
$request = ServerRequestFactory::fromGlobals()->withUri(new Uri('/non-rest'));
|
|
||||||
$test = $this;
|
|
||||||
$this->middleware->__invoke($request, new Response(), function ($req) use ($request, $test) {
|
|
||||||
$test->assertSame($request, $req);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue