mirror of
https://github.com/shlinkio/shlink.git
synced 2025-03-27 12:01:49 +03:00
Merge branch 'develop'
This commit is contained in:
commit
611a314cdf
6 changed files with 306 additions and 8 deletions
|
@ -73,5 +73,8 @@
|
||||||
"serve": "php -S 0.0.0.0:8000 -t public/",
|
"serve": "php -S 0.0.0.0:8000 -t public/",
|
||||||
"test": "phpunit --coverage-clover build/clover.xml",
|
"test": "phpunit --coverage-clover build/clover.xml",
|
||||||
"pretty-test": "phpunit --coverage-html build/coverage"
|
"pretty-test": "phpunit --coverage-html build/coverage"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"process-timeout": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
289
docs/swagger.yaml
Normal file
289
docs/swagger.yaml
Normal file
|
@ -0,0 +1,289 @@
|
||||||
|
swagger: '2.0'
|
||||||
|
info:
|
||||||
|
title: Shlink
|
||||||
|
description: Shlink, the self-hosted URL shortener
|
||||||
|
version: "1.2.0"
|
||||||
|
|
||||||
|
schemes:
|
||||||
|
- https
|
||||||
|
|
||||||
|
basePath: /rest
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
|
||||||
|
paths:
|
||||||
|
/authenticate:
|
||||||
|
post:
|
||||||
|
description: Performs an authentication
|
||||||
|
parameters:
|
||||||
|
- name: apiKey
|
||||||
|
in: formData
|
||||||
|
description: The API key to authenticate with
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: The authentication worked.
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
token:
|
||||||
|
type: string
|
||||||
|
description: The authentication token that needs to be sent in the Authorization header
|
||||||
|
400:
|
||||||
|
description: An API key was not provided.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
401:
|
||||||
|
description: The API key is incorrect, is disabled or has expired.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
500:
|
||||||
|
description: Unexpected error.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
/short-codes:
|
||||||
|
get:
|
||||||
|
description: Returns the list of short codes
|
||||||
|
parameters:
|
||||||
|
- name: page
|
||||||
|
in: query
|
||||||
|
description: The page to be displayed. Defaults to 1
|
||||||
|
required: false
|
||||||
|
type: integer
|
||||||
|
- name: Authorization
|
||||||
|
in: header
|
||||||
|
description: The authorization token with Bearer type
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: The list of short URLs
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
shortUrls:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/ShortUrl'
|
||||||
|
pagination:
|
||||||
|
$ref: '#/definitions/Pagination'
|
||||||
|
500:
|
||||||
|
description: Unexpected error.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
post:
|
||||||
|
description: Creates a new short code
|
||||||
|
parameters:
|
||||||
|
- name: longUrl
|
||||||
|
in: formData
|
||||||
|
description: The URL to parse
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
- name: tags
|
||||||
|
in: formData
|
||||||
|
description: The URL to parse
|
||||||
|
required: false
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
- name: Authorization
|
||||||
|
in: header
|
||||||
|
description: The authorization token with Bearer type
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: The result of parsing the long URL
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
longUrl:
|
||||||
|
type: string
|
||||||
|
description: The original long URL that has been parsed
|
||||||
|
shortUrl:
|
||||||
|
type: string
|
||||||
|
description: The generated short URL
|
||||||
|
shortCode:
|
||||||
|
type: string
|
||||||
|
description: the short code that is being used in the short URL
|
||||||
|
400:
|
||||||
|
description: The long URL was not provided or is invalid.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
500:
|
||||||
|
description: Unexpected error.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
/short-codes/{shortCode}:
|
||||||
|
get:
|
||||||
|
description: Get the long URL behind a short code.
|
||||||
|
parameters:
|
||||||
|
- name: shortCode
|
||||||
|
in: path
|
||||||
|
type: string
|
||||||
|
description: The short code to resolve.
|
||||||
|
required: true
|
||||||
|
- name: Authorization
|
||||||
|
in: header
|
||||||
|
description: The authorization token with Bearer type
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: The long URL behind a short code.
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
longUrl:
|
||||||
|
type: string
|
||||||
|
description: The original long URL behind the short code.
|
||||||
|
404:
|
||||||
|
description: No URL was found for provided short code.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
400:
|
||||||
|
description: Provided shortCode does not match the character set currently used by the app to generate short codes.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
500:
|
||||||
|
description: Unexpected error.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
/short-codes/{shortCode}/visits:
|
||||||
|
get:
|
||||||
|
description: Get the list of visits on provided short code.
|
||||||
|
parameters:
|
||||||
|
- name: shortCode
|
||||||
|
in: path
|
||||||
|
type: string
|
||||||
|
description: The shortCode from which we want to get the visits.
|
||||||
|
required: true
|
||||||
|
- name: Authorization
|
||||||
|
in: header
|
||||||
|
description: The authorization token with Bearer type
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: List of visits.
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
visits:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/Visit'
|
||||||
|
404:
|
||||||
|
description: The short code does not belong to any short URL.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
500:
|
||||||
|
description: Unexpected error.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
/short-codes/{shortCode}/tags:
|
||||||
|
put:
|
||||||
|
description: Edit the tags on provided short code.
|
||||||
|
parameters:
|
||||||
|
- name: shortCode
|
||||||
|
in: path
|
||||||
|
type: string
|
||||||
|
description: The shortCode in which we want to edit tags.
|
||||||
|
required: true
|
||||||
|
- name: tags
|
||||||
|
in: formData
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
description: The list of tags to set to the short URL.
|
||||||
|
required: true
|
||||||
|
- name: Authorization
|
||||||
|
in: header
|
||||||
|
description: The authorization token with Bearer type
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: List of tags.
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
tags:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
400:
|
||||||
|
description: The request body does not contain a "tags" param with array type.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
404:
|
||||||
|
description: No short URL was found for provided short code.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
500:
|
||||||
|
description: Unexpected error.
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Error'
|
||||||
|
|
||||||
|
definitions:
|
||||||
|
ShortUrl:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
shortCode:
|
||||||
|
type: string
|
||||||
|
description: The short code for this short URL.
|
||||||
|
originalUrl:
|
||||||
|
type: string
|
||||||
|
description: The original long URL.
|
||||||
|
dateCreated:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
description: The date in which the short URL was created in ISO format.
|
||||||
|
visitsCount:
|
||||||
|
type: integer
|
||||||
|
description: The number of visits that this short URL has recieved.
|
||||||
|
tags:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
description: A list of tags applied to this short URL
|
||||||
|
|
||||||
|
Visit:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
referer:
|
||||||
|
type: string
|
||||||
|
date:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
remoteAddr:
|
||||||
|
type: string
|
||||||
|
userAgent:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
Error:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
code:
|
||||||
|
type: string
|
||||||
|
description: A machine unique code
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
description: A human-friendly error message
|
||||||
|
|
||||||
|
Pagination:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
currentPage:
|
||||||
|
type: integer
|
||||||
|
description: The number of current page being displayed.
|
||||||
|
pagesCount:
|
||||||
|
type: integer
|
||||||
|
description: The total number of pages that can be displayed.
|
|
@ -29,12 +29,6 @@ class VisitRepository extends EntityRepository implements VisitRepositoryInterfa
|
||||||
$shortUrl = $shortUrl instanceof ShortUrl
|
$shortUrl = $shortUrl instanceof ShortUrl
|
||||||
? $shortUrl
|
? $shortUrl
|
||||||
: $this->getEntityManager()->find(ShortUrl::class, $shortUrl);
|
: $this->getEntityManager()->find(ShortUrl::class, $shortUrl);
|
||||||
if (! isset($dateRange) || $dateRange->isEmpty()) {
|
|
||||||
$startDate = $shortUrl->getDateCreated();
|
|
||||||
$endDate = clone $startDate;
|
|
||||||
$endDate->add(new \DateInterval('P2D'));
|
|
||||||
$dateRange = new DateRange($startDate, $endDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
$qb = $this->createQueryBuilder('v');
|
$qb = $this->createQueryBuilder('v');
|
||||||
$qb->where($qb->expr()->eq('v.shortUrl', ':shortUrl'))
|
$qb->where($qb->expr()->eq('v.shortUrl', ':shortUrl'))
|
||||||
|
|
|
@ -66,7 +66,7 @@ class AuthenticateAction extends AbstractRestAction
|
||||||
|
|
||||||
// Authenticate using provided API key
|
// Authenticate using provided API key
|
||||||
$apiKey = $this->apiKeyService->getByKey($authData['apiKey']);
|
$apiKey = $this->apiKeyService->getByKey($authData['apiKey']);
|
||||||
if (! $apiKey->isValid()) {
|
if (! isset($apiKey) || ! $apiKey->isValid()) {
|
||||||
return new JsonResponse([
|
return new JsonResponse([
|
||||||
'error' => RestUtils::INVALID_API_KEY_ERROR,
|
'error' => RestUtils::INVALID_API_KEY_ERROR,
|
||||||
'message' => $this->translator->translate('Provided API key does not exist or is invalid.'),
|
'message' => $this->translator->translate('Provided API key does not exist or is invalid.'),
|
||||||
|
|
|
@ -13,6 +13,7 @@ use Shlinkio\Shlink\Rest\Util\RestUtils;
|
||||||
use Zend\Diactoros\Response\JsonResponse;
|
use Zend\Diactoros\Response\JsonResponse;
|
||||||
use Zend\Expressive\Router\RouteResult;
|
use Zend\Expressive\Router\RouteResult;
|
||||||
use Zend\I18n\Translator\TranslatorInterface;
|
use Zend\I18n\Translator\TranslatorInterface;
|
||||||
|
use Zend\Stdlib\ErrorHandler;
|
||||||
use Zend\Stratigility\MiddlewareInterface;
|
use Zend\Stratigility\MiddlewareInterface;
|
||||||
|
|
||||||
class CheckAuthenticationMiddleware implements MiddlewareInterface
|
class CheckAuthenticationMiddleware implements MiddlewareInterface
|
||||||
|
@ -117,9 +118,11 @@ class CheckAuthenticationMiddleware implements MiddlewareInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
ErrorHandler::start();
|
||||||
if (! $this->jwtService->verify($jwt)) {
|
if (! $this->jwtService->verify($jwt)) {
|
||||||
return $this->createTokenErrorResponse();
|
return $this->createTokenErrorResponse();
|
||||||
}
|
}
|
||||||
|
ErrorHandler::stop(true);
|
||||||
|
|
||||||
// Update the token expiration and continue to next middleware
|
// Update the token expiration and continue to next middleware
|
||||||
$jwt = $this->jwtService->refresh($jwt);
|
$jwt = $this->jwtService->refresh($jwt);
|
||||||
|
@ -131,6 +134,14 @@ class CheckAuthenticationMiddleware implements MiddlewareInterface
|
||||||
} catch (AuthenticationException $e) {
|
} catch (AuthenticationException $e) {
|
||||||
$this->logger->warning('Tried to access API with an invalid JWT.' . PHP_EOL . $e);
|
$this->logger->warning('Tried to access API with an invalid JWT.' . PHP_EOL . $e);
|
||||||
return $this->createTokenErrorResponse();
|
return $this->createTokenErrorResponse();
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$this->logger->warning('Unexpected error occurred.' . PHP_EOL . $e);
|
||||||
|
return $this->createTokenErrorResponse();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$this->logger->warning('Unexpected error occurred.' . PHP_EOL . $e);
|
||||||
|
return $this->createTokenErrorResponse();
|
||||||
|
} finally {
|
||||||
|
ErrorHandler::clean();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,8 @@ class CrossDomainMiddleware implements MiddlewareInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add Allow-Origin header
|
// Add Allow-Origin header
|
||||||
$response = $response->withHeader('Access-Control-Allow-Origin', $request->getHeader('Origin'));
|
$response = $response->withHeader('Access-Control-Allow-Origin', $request->getHeader('Origin'))
|
||||||
|
->withHeader('Access-Control-Expose-Headers', 'Authorization');
|
||||||
if ($request->getMethod() !== 'OPTIONS') {
|
if ($request->getMethod() !== 'OPTIONS') {
|
||||||
return $response;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue