mirror of
https://github.com/grocy/grocy.git
synced 2025-09-16 09:51:30 +00:00
Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
99b2a84667 | ||
|
9bd6aac09c | ||
|
7be35a90c1 | ||
|
eae5b8bad9 | ||
|
0c85342404 |
54
app.php
54
app.php
@@ -3,46 +3,42 @@
|
||||
use \Psr\Http\Message\ServerRequestInterface as Request;
|
||||
use \Psr\Http\Message\ResponseInterface as Response;
|
||||
|
||||
use \Grocy\Middleware\SessionAuthMiddleware;
|
||||
use \Grocy\Helpers\UrlManager;
|
||||
use \Grocy\Services\ApplicationService;
|
||||
use \Grocy\Controllers\LoginController;
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/data/config.php';
|
||||
|
||||
// Setup base application
|
||||
if (PHP_SAPI !== 'cli')
|
||||
{
|
||||
$appContainer = new \Slim\Container([
|
||||
'settings' => [
|
||||
'displayErrorDetails' => true,
|
||||
'determineRouteBeforeAppMiddleware' => true
|
||||
],
|
||||
'view' => function($container)
|
||||
{
|
||||
return new \Slim\Views\Blade(__DIR__ . '/views', __DIR__ . '/data/viewcache');
|
||||
},
|
||||
'UrlManager' => function($container)
|
||||
{
|
||||
return new UrlManager(BASE_URL);
|
||||
}
|
||||
]);
|
||||
$appContainer = new \Slim\Container([
|
||||
'settings' => [
|
||||
'displayErrorDetails' => true,
|
||||
'determineRouteBeforeAppMiddleware' => true
|
||||
],
|
||||
'view' => function($container)
|
||||
{
|
||||
return new \Slim\Views\Blade(__DIR__ . '/views', __DIR__ . '/data/viewcache');
|
||||
},
|
||||
'LoginControllerInstance' => function($container)
|
||||
{
|
||||
return new LoginController($container, 'grocy_session');
|
||||
},
|
||||
'UrlManager' => function($container)
|
||||
{
|
||||
return new UrlManager(BASE_URL);
|
||||
},
|
||||
'ApiKeyHeaderName' => function($container)
|
||||
{
|
||||
return 'GROCY-API-KEY';
|
||||
}
|
||||
]);
|
||||
$app = new \Slim\App($appContainer);
|
||||
|
||||
$app = new \Slim\App($appContainer);
|
||||
}
|
||||
else
|
||||
if (PHP_SAPI === 'cli')
|
||||
{
|
||||
$app = new \Slim\App();
|
||||
$app->add(\pavlakis\cli\CliRequest::class);
|
||||
}
|
||||
|
||||
// Add session handling if this is not a demo installation
|
||||
$applicationService = new ApplicationService();
|
||||
if (!$applicationService->IsDemoInstallation())
|
||||
{
|
||||
$app->add(SessionAuthMiddleware::class);
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/routes.php';
|
||||
|
||||
$app->run();
|
||||
|
@@ -17,6 +17,8 @@
|
||||
"jquery-timeago": "^1.6.1",
|
||||
"toastr": "^2.1.3",
|
||||
"tagmanager": "^3.0.2",
|
||||
"eonasdan-bootstrap-datetimepicker": "^4.17.47"
|
||||
"eonasdan-bootstrap-datetimepicker": "^4.17.47",
|
||||
"swagger-ui": "^3.13.4",
|
||||
"jquery-ui": "^1.12.1"
|
||||
}
|
||||
}
|
||||
|
@@ -1,9 +1,11 @@
|
||||
{
|
||||
"require": {
|
||||
"php": ">=7.0",
|
||||
"slim/slim": "^3.8",
|
||||
"morris/lessql": "^0.3.4",
|
||||
"pavlakis/slim-cli": "^1.0",
|
||||
"rubellum/slim-blade-view": "^0.1.1"
|
||||
"rubellum/slim-blade-view": "^0.1.1",
|
||||
"tuupola/cors-middleware": "^0.7.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
430
composer.lock
generated
430
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "12ebab60e283dfdab831d1cc22430d05",
|
||||
"content-hash": "42031c0b205b7ce7efb4b6eb95a0096a",
|
||||
"packages": [
|
||||
{
|
||||
"name": "container-interop/container-interop",
|
||||
@@ -104,9 +104,61 @@
|
||||
],
|
||||
"time": "2018-01-09T20:05:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "http-interop/http-factory",
|
||||
"version": "0.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/http-interop/http-factory.git",
|
||||
"reference": "c2587cc0a6f74987fefb5b8074acfd32c69a4b0f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/http-interop/http-factory/zipball/c2587cc0a6f74987fefb5b8074acfd32c69a4b0f",
|
||||
"reference": "c2587cc0a6f74987fefb5b8074acfd32c69a4b0f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0",
|
||||
"psr/http-message": "^1.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Interop\\Http\\Factory\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP message factories",
|
||||
"keywords": [
|
||||
"factory",
|
||||
"http",
|
||||
"message",
|
||||
"psr",
|
||||
"psr-17",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response"
|
||||
],
|
||||
"time": "2017-03-24T14:48:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "illuminate/container",
|
||||
"version": "v5.6.16",
|
||||
"version": "v5.6.17",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/illuminate/container.git",
|
||||
@@ -150,7 +202,7 @@
|
||||
},
|
||||
{
|
||||
"name": "illuminate/contracts",
|
||||
"version": "v5.6.16",
|
||||
"version": "v5.6.17",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/illuminate/contracts.git",
|
||||
@@ -194,7 +246,7 @@
|
||||
},
|
||||
{
|
||||
"name": "illuminate/events",
|
||||
"version": "v5.6.16",
|
||||
"version": "v5.6.17",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/illuminate/events.git",
|
||||
@@ -239,7 +291,7 @@
|
||||
},
|
||||
{
|
||||
"name": "illuminate/filesystem",
|
||||
"version": "v5.6.16",
|
||||
"version": "v5.6.17",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/illuminate/filesystem.git",
|
||||
@@ -291,16 +343,16 @@
|
||||
},
|
||||
{
|
||||
"name": "illuminate/support",
|
||||
"version": "v5.6.16",
|
||||
"version": "v5.6.17",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/illuminate/support.git",
|
||||
"reference": "fad0669f858423679497a17f973261cc32f9a5a8"
|
||||
"reference": "cc8d6f5cef3a901de6bb7d1b362102a6db001085"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/illuminate/support/zipball/fad0669f858423679497a17f973261cc32f9a5a8",
|
||||
"reference": "fad0669f858423679497a17f973261cc32f9a5a8",
|
||||
"url": "https://api.github.com/repos/illuminate/support/zipball/cc8d6f5cef3a901de6bb7d1b362102a6db001085",
|
||||
"reference": "cc8d6f5cef3a901de6bb7d1b362102a6db001085",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -344,11 +396,11 @@
|
||||
],
|
||||
"description": "The Illuminate Support package.",
|
||||
"homepage": "https://laravel.com",
|
||||
"time": "2018-04-05T21:19:22+00:00"
|
||||
"time": "2018-04-17T12:26:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "illuminate/view",
|
||||
"version": "v5.6.16",
|
||||
"version": "v5.6.17",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/illuminate/view.git",
|
||||
@@ -443,17 +495,72 @@
|
||||
"time": "2018-01-27T13:18:21+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nesbot/carbon",
|
||||
"version": "1.25.0",
|
||||
"name": "neomerx/cors-psr7",
|
||||
"version": "v1.0.12",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/briannesbitt/Carbon.git",
|
||||
"reference": "cbcf13da0b531767e39eb86e9687f5deba9857b4"
|
||||
"url": "https://github.com/neomerx/cors-psr7.git",
|
||||
"reference": "24944f39483d1a89f66ae9d58cca9f82b8815b35"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/cbcf13da0b531767e39eb86e9687f5deba9857b4",
|
||||
"reference": "cbcf13da0b531767e39eb86e9687f5deba9857b4",
|
||||
"url": "https://api.github.com/repos/neomerx/cors-psr7/zipball/24944f39483d1a89f66ae9d58cca9f82b8815b35",
|
||||
"reference": "24944f39483d1a89f66ae9d58cca9f82b8815b35",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.6.0",
|
||||
"psr/http-message": "^1.0",
|
||||
"psr/log": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^0.9.9",
|
||||
"phpunit/phpunit": "^5.7",
|
||||
"scrutinizer/ocular": "^1.1",
|
||||
"squizlabs/php_codesniffer": "^3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Neomerx\\Cors\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Apache-2.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "neomerx",
|
||||
"email": "info@neomerx.com"
|
||||
}
|
||||
],
|
||||
"description": "Framework agnostic (PSR-7) CORS implementation (www.w3.org/TR/cors/)",
|
||||
"homepage": "https://github.com/neomerx/cors-psr7",
|
||||
"keywords": [
|
||||
"Cross Origin Resource Sharing",
|
||||
"Cross-Origin Resource Sharing",
|
||||
"cors",
|
||||
"neomerx",
|
||||
"psr-7",
|
||||
"psr7",
|
||||
"w3.org",
|
||||
"www.w3.org"
|
||||
],
|
||||
"time": "2017-09-03T22:31:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nesbot/carbon",
|
||||
"version": "1.26.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/briannesbitt/Carbon.git",
|
||||
"reference": "e3d9014279133a3cccc01f6a691322a2d5a6a87b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/e3d9014279133a3cccc01f6a691322a2d5a6a87b",
|
||||
"reference": "e3d9014279133a3cccc01f6a691322a2d5a6a87b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -465,14 +572,9 @@
|
||||
"phpunit/phpunit": "^4.8.35 || ^5.7"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.23-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Carbon\\": "src/Carbon/"
|
||||
"": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
@@ -493,7 +595,7 @@
|
||||
"datetime",
|
||||
"time"
|
||||
],
|
||||
"time": "2018-03-19T15:50:49+00:00"
|
||||
"time": "2018-04-17T15:35:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nikic/fast-route",
|
||||
@@ -780,6 +882,112 @@
|
||||
],
|
||||
"time": "2016-08-06T14:39:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-server-handler",
|
||||
"version": "1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-server-handler.git",
|
||||
"reference": "439d92054dc06097f2406ec074a2627839955a02"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-server-handler/zipball/439d92054dc06097f2406ec074a2627839955a02",
|
||||
"reference": "439d92054dc06097f2406ec074a2627839955a02",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.0",
|
||||
"psr/http-message": "^1.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Server\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP server-side request handler",
|
||||
"keywords": [
|
||||
"handler",
|
||||
"http",
|
||||
"http-interop",
|
||||
"psr",
|
||||
"psr-15",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response",
|
||||
"server"
|
||||
],
|
||||
"time": "2018-01-22T17:04:15+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-server-middleware",
|
||||
"version": "1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-server-middleware.git",
|
||||
"reference": "ea17eb1fb2c8df6db919cc578451a8013c6a0ae5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-server-middleware/zipball/ea17eb1fb2c8df6db919cc578451a8013c6a0ae5",
|
||||
"reference": "ea17eb1fb2c8df6db919cc578451a8013c6a0ae5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.0",
|
||||
"psr/http-message": "^1.0",
|
||||
"psr/http-server-handler": "^1.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Server\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP server-side middleware",
|
||||
"keywords": [
|
||||
"http",
|
||||
"http-interop",
|
||||
"middleware",
|
||||
"psr",
|
||||
"psr-15",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response"
|
||||
],
|
||||
"time": "2018-01-22T17:08:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/log",
|
||||
"version": "1.0.2",
|
||||
@@ -927,16 +1135,16 @@
|
||||
},
|
||||
{
|
||||
"name": "slim/slim",
|
||||
"version": "3.9.2",
|
||||
"version": "3.10.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/slimphp/Slim.git",
|
||||
"reference": "4086d0106cf5a7135c69fce4161fe355a8feb118"
|
||||
"reference": "d8aabeacc3688b25e2f2dd2db91df91ec6fdd748"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/slimphp/Slim/zipball/4086d0106cf5a7135c69fce4161fe355a8feb118",
|
||||
"reference": "4086d0106cf5a7135c69fce4161fe355a8feb118",
|
||||
"url": "https://api.github.com/repos/slimphp/Slim/zipball/d8aabeacc3688b25e2f2dd2db91df91ec6fdd748",
|
||||
"reference": "d8aabeacc3688b25e2f2dd2db91df91ec6fdd748",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -994,7 +1202,7 @@
|
||||
"micro",
|
||||
"router"
|
||||
],
|
||||
"time": "2017-11-26T19:13:09+00:00"
|
||||
"time": "2018-04-19T19:29:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/debug",
|
||||
@@ -1227,6 +1435,166 @@
|
||||
"description": "Symfony Translation Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2018-02-22T10:50:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "tuupola/callable-handler",
|
||||
"version": "0.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/tuupola/callable-handler.git",
|
||||
"reference": "5141efa1e974687a3fa53338811a988198f50662"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/tuupola/callable-handler/zipball/5141efa1e974687a3fa53338811a988198f50662",
|
||||
"reference": "5141efa1e974687a3fa53338811a988198f50662",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.0",
|
||||
"psr/http-server-middleware": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"codedungeon/phpunit-result-printer": "^0.4.4",
|
||||
"overtrue/phplint": "^1.0",
|
||||
"phpunit/phpunit": "^6.5",
|
||||
"squizlabs/php_codesniffer": "^3.2",
|
||||
"tuupola/http-factory": "^0.3.0",
|
||||
"zendframework/zend-diactoros": "^1.6"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Tuupola\\Middleware\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mika Tuupola",
|
||||
"email": "tuupola@appelsiini.net",
|
||||
"homepage": "https://appelsiini.net/",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Compatibility layer for PSR-7 double pass and PSR-15 middlewares.",
|
||||
"homepage": "https://github.com/tuupola/callable-handler",
|
||||
"keywords": [
|
||||
"middleware",
|
||||
"psr-15",
|
||||
"psr-7"
|
||||
],
|
||||
"time": "2018-01-23T04:07:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "tuupola/cors-middleware",
|
||||
"version": "0.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/tuupola/cors-middleware.git",
|
||||
"reference": "b0e2b7acacf22acae6ba029ee424fd6c073bb443"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/tuupola/cors-middleware/zipball/b0e2b7acacf22acae6ba029ee424fd6c073bb443",
|
||||
"reference": "b0e2b7acacf22acae6ba029ee424fd6c073bb443",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"neomerx/cors-psr7": "^1.0",
|
||||
"php": "^7.1",
|
||||
"psr/http-server-middleware": "^1.0",
|
||||
"tuupola/callable-handler": "^0.3.0",
|
||||
"tuupola/http-factory": "^0.3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"codedungeon/phpunit-result-printer": "^0.4.4",
|
||||
"equip/dispatch": "dev-approved-psr15 as 1.0.x-dev",
|
||||
"overtrue/phplint": "^1.0",
|
||||
"phpunit/phpunit": "^6.5",
|
||||
"squizlabs/php_codesniffer": "^3.2",
|
||||
"zendframework/zend-diactoros": "^1.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Tuupola\\Middleware\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mika Tuupola",
|
||||
"email": "tuupola@appelsiini.net",
|
||||
"homepage": "http://www.appelsiini.net/",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "PSR-7 and PSR-15 CORS middleware",
|
||||
"homepage": "https://github.com/tuupola/cors-middleware",
|
||||
"keywords": [
|
||||
"cors",
|
||||
"middleware",
|
||||
"psr-15",
|
||||
"psr-7"
|
||||
],
|
||||
"time": "2018-01-25T02:29:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "tuupola/http-factory",
|
||||
"version": "0.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/tuupola/http-factory.git",
|
||||
"reference": "57b2e19ff3f4af0bbee4e31fd282689be351f1ad"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/tuupola/http-factory/zipball/57b2e19ff3f4af0bbee4e31fd282689be351f1ad",
|
||||
"reference": "57b2e19ff3f4af0bbee4e31fd282689be351f1ad",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"http-interop/http-factory": "^0.3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"http-interop/http-factory-tests": "^0.3.0",
|
||||
"overtrue/phplint": "^0.2.1",
|
||||
"phpunit/phpunit": "^5.7",
|
||||
"squizlabs/php_codesniffer": "^3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Tuupola\\Http\\Factory\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mika Tuupola",
|
||||
"email": "tuupola@appelsiini.net",
|
||||
"homepage": "http://www.appelsiini.net/",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Lightweight autodiscovering PSR-17 HTTP factories",
|
||||
"homepage": "https://github.com/tuupola/http-factory",
|
||||
"keywords": [
|
||||
"http",
|
||||
"psr-17",
|
||||
"psr-7"
|
||||
],
|
||||
"time": "2017-07-15T22:03:15+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
@@ -1235,6 +1603,8 @@
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform": {
|
||||
"php": ">=7.0"
|
||||
},
|
||||
"platform-dev": []
|
||||
}
|
||||
|
@@ -9,13 +9,15 @@ use \Grocy\Services\DemoDataGeneratorService;
|
||||
|
||||
class LoginController extends BaseController
|
||||
{
|
||||
public function __construct(\Slim\Container $container)
|
||||
public function __construct(\Slim\Container $container, string $sessionCookieName)
|
||||
{
|
||||
parent::__construct($container);
|
||||
$this->SessionService = new SessionService();
|
||||
$this->SessionCookieName = $sessionCookieName;
|
||||
}
|
||||
|
||||
protected $SessionService;
|
||||
protected $SessionCookieName;
|
||||
|
||||
public function ProcessLogin(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
||||
{
|
||||
@@ -25,7 +27,7 @@ class LoginController extends BaseController
|
||||
if ($postParams['username'] === HTTP_USER && $postParams['password'] === HTTP_PASSWORD)
|
||||
{
|
||||
$sessionKey = $this->SessionService->CreateSession();
|
||||
setcookie('grocy_session', $sessionKey, time() + 31536000); // Cookie expires in 1 year, but session validity is up to SessionService
|
||||
setcookie($this->SessionCookieName, $sessionKey, time() + 31536000); // Cookie expires in 1 year, but session validity is up to SessionService
|
||||
|
||||
return $response->withRedirect($this->AppContainer->UrlManager->ConstructUrl('/'));
|
||||
}
|
||||
@@ -47,7 +49,7 @@ class LoginController extends BaseController
|
||||
|
||||
public function Logout(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
||||
{
|
||||
$this->SessionService->RemoveSession($_COOKIE['grocy_session']);
|
||||
$this->SessionService->RemoveSession($_COOKIE[$this->SessionCookieName]);
|
||||
return $response->withRedirect($this->AppContainer->UrlManager->ConstructUrl('/'));
|
||||
}
|
||||
|
||||
@@ -66,4 +68,9 @@ class LoginController extends BaseController
|
||||
|
||||
return $response->withRedirect($this->AppContainer->UrlManager->ConstructUrl('/stockoverview'));
|
||||
}
|
||||
|
||||
public function GetSessionCookieName()
|
||||
{
|
||||
return $this->SessionCookieName;
|
||||
}
|
||||
}
|
||||
|
48
controllers/OpenApiController.php
Normal file
48
controllers/OpenApiController.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Grocy\Controllers;
|
||||
|
||||
use \Grocy\Services\ApplicationService;
|
||||
use \Grocy\Services\ApiKeyService;
|
||||
|
||||
class OpenApiController extends BaseApiController
|
||||
{
|
||||
public function __construct(\Slim\Container $container)
|
||||
{
|
||||
parent::__construct($container);
|
||||
$this->ApiKeyService = new ApiKeyService();
|
||||
}
|
||||
|
||||
protected $ApiKeyService;
|
||||
|
||||
public function DocumentationUi(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
||||
{
|
||||
return $this->AppContainer->view->render($response, 'openapiui');
|
||||
}
|
||||
|
||||
public function DocumentationSpec(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
||||
{
|
||||
$applicationService = new ApplicationService();
|
||||
|
||||
$specJson = json_decode(file_get_contents(__DIR__ . '/../grocy.openapi.json'));
|
||||
$specJson->info->version = $applicationService->GetInstalledVersion();
|
||||
$specJson->info->description = str_replace('PlaceHolderManageApiKeysUrl', $this->AppContainer->UrlManager->ConstructUrl('/manageapikeys'), $specJson->info->description);
|
||||
$specJson->servers[0]->url = $this->AppContainer->UrlManager->ConstructUrl('/api');
|
||||
|
||||
return $this->ApiResponse($specJson);
|
||||
}
|
||||
|
||||
public function ApiKeysList(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
||||
{
|
||||
return $this->AppContainer->view->render($response, 'manageapikeys', [
|
||||
'apiKeys' => $this->Database->api_keys()
|
||||
]);
|
||||
}
|
||||
|
||||
public function CreateNewApiKey(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
||||
{
|
||||
$newApiKey = $this->ApiKeyService->CreateApiKey();
|
||||
$newApiKeyId = $this->ApiKeyService->GetApiKeyId($newApiKey);
|
||||
return $response->withRedirect($this->AppContainer->UrlManager->ConstructUrl("/manageapikeys?CreatedApiKeyId=$newApiKeyId"));
|
||||
}
|
||||
}
|
1137
grocy.openapi.json
Normal file
1137
grocy.openapi.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,8 +4,16 @@ namespace Grocy\Helpers;
|
||||
|
||||
class UrlManager
|
||||
{
|
||||
public function __construct(string $basePath) {
|
||||
$this->BasePath = $basePath;
|
||||
public function __construct(string $basePath)
|
||||
{
|
||||
if ($basePath === '/')
|
||||
{
|
||||
$this->BasePath = $this->GetBaseUrl();
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->BasePath = $basePath;
|
||||
}
|
||||
}
|
||||
|
||||
protected $BasePath;
|
||||
@@ -14,4 +22,9 @@ class UrlManager
|
||||
{
|
||||
return rtrim($this->BasePath, '/') . $relativePath;
|
||||
}
|
||||
|
||||
private function GetBaseUrl()
|
||||
{
|
||||
return (isset($_SERVER['HTTPS']) ? "https" : "http") . "://$_SERVER[HTTP_HOST]";
|
||||
}
|
||||
}
|
||||
|
@@ -61,3 +61,14 @@ function GetClassConstants($className)
|
||||
$r = new ReflectionClass($className);
|
||||
return $r->getConstants();
|
||||
}
|
||||
|
||||
function RandomString($length, $allowedChars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
|
||||
{
|
||||
$randomString = '';
|
||||
for ($i = 0; $i < $length; $i++)
|
||||
{
|
||||
$randomString .= $allowedChars[rand(0, strlen($allowedChars) - 1)];
|
||||
}
|
||||
|
||||
return $randomString;
|
||||
}
|
||||
|
@@ -101,6 +101,17 @@ return array(
|
||||
'Are you sure to delete quantity unit "#1"?' => 'Mengeneinheit "#1" wirklich löschen?',
|
||||
'Are you sure to delete product "#1"?' => 'Produkt "#1" wirklich löschen?',
|
||||
'Are you sure to delete location "#1"?' => 'Standort "#1" wirklich löschen?',
|
||||
'Manage API keys' => 'API-Keys verwalten',
|
||||
'REST API & data model documentation' => 'REST-API & Datenmodell Dokumentation',
|
||||
'API keys' => 'API-Keys',
|
||||
'Create new API key' => 'Neuen API-Key erstellen',
|
||||
'API key' => 'API-Key',
|
||||
'Expires' => 'Läuft ab',
|
||||
'Created' => 'Erstellt',
|
||||
'This product is not in stock' => 'Dieses Produkt ist nicht vorrätig',
|
||||
'This means #1 will be added to stock' => 'Das bedeutet #1 wird dem Bestand hinzugefügt',
|
||||
'This means #1 will be removed from stock' => 'Das bedeutet #1 wird aus dem Bestand entfernt',
|
||||
'This means it is estimated that a new execution of this habit is tracked #1 days after the last was tracked' => 'Das bedeutet, dass eine erneute Ausführung der Gewohnheit #1 Tage nach der letzten Ausführung geplant wird',
|
||||
|
||||
//Constants
|
||||
'manually' => 'Manuell',
|
||||
|
58
middleware/ApiKeyAuthMiddleware.php
Normal file
58
middleware/ApiKeyAuthMiddleware.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace Grocy\Middleware;
|
||||
|
||||
use \Grocy\Services\SessionService;
|
||||
use \Grocy\Services\ApiKeyService;
|
||||
|
||||
class ApiKeyAuthMiddleware extends BaseMiddleware
|
||||
{
|
||||
public function __construct(\Slim\Container $container, string $sessionCookieName, string $apiKeyHeaderName)
|
||||
{
|
||||
parent::__construct($container);
|
||||
$this->SessionCookieName = $sessionCookieName;
|
||||
$this->ApiKeyHeaderName = $apiKeyHeaderName;
|
||||
}
|
||||
|
||||
protected $SessionCookieName;
|
||||
protected $ApiKeyHeaderName;
|
||||
|
||||
public function __invoke(\Slim\Http\Request $request, \Slim\Http\Response $response, callable $next)
|
||||
{
|
||||
$route = $request->getAttribute('route');
|
||||
$routeName = $route->getName();
|
||||
|
||||
if ($this->ApplicationService->IsDemoInstallation())
|
||||
{
|
||||
$response = $next($request, $response);
|
||||
}
|
||||
else
|
||||
{
|
||||
$validSession = true;
|
||||
$validApiKey = true;
|
||||
|
||||
$sessionService = new SessionService();
|
||||
if (!isset($_COOKIE[$this->SessionCookieName]) || !$sessionService->IsValidSession($_COOKIE[$this->SessionCookieName]))
|
||||
{
|
||||
$validSession = false;
|
||||
}
|
||||
|
||||
$apiKeyService = new ApiKeyService();
|
||||
if (!$request->hasHeader($this->ApiKeyHeaderName) || !$apiKeyService->IsValidApiKey($request->getHeaderLine($this->ApiKeyHeaderName)))
|
||||
{
|
||||
$validApiKey = false;
|
||||
}
|
||||
|
||||
if (!$validSession && !$validApiKey)
|
||||
{
|
||||
$response = $response->withStatus(401);
|
||||
}
|
||||
else
|
||||
{
|
||||
$response = $next($request, $response);
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
@@ -2,11 +2,16 @@
|
||||
|
||||
namespace Grocy\Middleware;
|
||||
|
||||
use \Grocy\Services\ApplicationService;
|
||||
|
||||
class BaseMiddleware
|
||||
{
|
||||
public function __construct(\Slim\Container $container) {
|
||||
public function __construct(\Slim\Container $container)
|
||||
{
|
||||
$this->AppContainer = $container;
|
||||
$this->ApplicationService = new ApplicationService();
|
||||
}
|
||||
|
||||
protected $AppContainer;
|
||||
protected $ApplicationService;
|
||||
}
|
||||
|
@@ -13,7 +13,7 @@ class CliMiddleware extends BaseMiddleware
|
||||
}
|
||||
else
|
||||
{
|
||||
$response = $next($request, $response, $next);
|
||||
$response = $next($request, $response);
|
||||
return $response->withHeader('Content-Type', 'text/plain');
|
||||
}
|
||||
}
|
||||
|
@@ -6,7 +6,7 @@ class JsonMiddleware extends BaseMiddleware
|
||||
{
|
||||
public function __invoke(\Slim\Http\Request $request, \Slim\Http\Response $response, callable $next)
|
||||
{
|
||||
$response = $next($request, $response, $next);
|
||||
$response = $next($request, $response);
|
||||
return $response->withHeader('Content-Type', 'application/json');
|
||||
}
|
||||
}
|
||||
|
@@ -6,19 +6,27 @@ use \Grocy\Services\SessionService;
|
||||
|
||||
class SessionAuthMiddleware extends BaseMiddleware
|
||||
{
|
||||
public function __construct(\Slim\Container $container, string $sessionCookieName)
|
||||
{
|
||||
parent::__construct($container);
|
||||
$this->SessionCookieName = $sessionCookieName;
|
||||
}
|
||||
|
||||
protected $SessionCookieName;
|
||||
|
||||
public function __invoke(\Slim\Http\Request $request, \Slim\Http\Response $response, callable $next)
|
||||
{
|
||||
$route = $request->getAttribute('route');
|
||||
$routeName = $route->getName();
|
||||
|
||||
if ($routeName === 'root')
|
||||
if ($routeName === 'root' || $this->ApplicationService->IsDemoInstallation())
|
||||
{
|
||||
$response = $next($request, $response);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sessionService = new SessionService();
|
||||
if ((!isset($_COOKIE['grocy_session']) || !$sessionService->IsValidSession($_COOKIE['grocy_session'])) && $routeName !== 'login')
|
||||
if ((!isset($_COOKIE[$this->SessionCookieName]) || !$sessionService->IsValidSession($_COOKIE[$this->SessionCookieName])) && $routeName !== 'login')
|
||||
{
|
||||
$response = $response->withRedirect($this->AppContainer->UrlManager->ConstructUrl('/login'));
|
||||
}
|
||||
|
7
migrations/0022.sql
Normal file
7
migrations/0022.sql
Normal file
@@ -0,0 +1,7 @@
|
||||
CREATE TABLE api_keys (
|
||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
|
||||
api_key TEXT NOT NULL UNIQUE,
|
||||
expires DATETIME,
|
||||
last_used DATETIME,
|
||||
row_created_timestamp DATETIME DEFAULT (datetime('now', 'localtime'))
|
||||
)
|
1
migrations/0023.sql
Normal file
1
migrations/0023.sql
Normal file
@@ -0,0 +1 @@
|
||||
DELETE FROM sessions
|
2
migrations/0024.sql
Normal file
2
migrations/0024.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER TABLE sessions
|
||||
ADD COLUMN last_used DATETIME
|
@@ -169,3 +169,31 @@ a.discrete-link:focus {
|
||||
.well {
|
||||
background-color: #e5e5e5;
|
||||
}
|
||||
|
||||
.nav > li.disabled > a,
|
||||
.navbar-default .navbar-nav > .disabled > a
|
||||
{
|
||||
color: #a7a7a7;
|
||||
}
|
||||
|
||||
#toast-container > div {
|
||||
opacity: 1;
|
||||
filter: alpha(opacity=100);
|
||||
}
|
||||
|
||||
.toast-success {
|
||||
background-color: #4c994c;
|
||||
}
|
||||
|
||||
#toast-container > div {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.navbar-default .navbar-nav > .open > a {
|
||||
background-color: #d6d6d6 !important;
|
||||
}
|
||||
|
||||
.dropdown-menu > li > a:hover,
|
||||
.dropdown-menu > li > a:focus {
|
||||
background-color: #e5e5e5 !important;
|
||||
}
|
||||
|
@@ -28,6 +28,13 @@ if (!Grocy.ActiveNav.isEmpty())
|
||||
$.timeago.settings.allowFuture = true;
|
||||
$('time.timeago').timeago();
|
||||
|
||||
toastr.options = {
|
||||
toastClass: 'alert',
|
||||
closeButton: true,
|
||||
timeOut: 20000,
|
||||
extendedTimeOut: 5000
|
||||
};
|
||||
|
||||
Grocy.Api = { };
|
||||
Grocy.Api.Get = function(apiFunction, success, error)
|
||||
{
|
||||
|
@@ -1,7 +1,10 @@
|
||||
$(document).on('click', '.battery-delete-button', function(e)
|
||||
{
|
||||
var objectName = $(e.currentTarget).attr('data-battery-name');
|
||||
var objectId = $(e.currentTarget).attr('data-battery-id');
|
||||
|
||||
bootbox.confirm({
|
||||
message: L('Are you sure to delete battery "#1"?', $(e.currentTarget).attr('data-battery-name')),
|
||||
message: L('Are you sure to delete battery "#1"?', objectName),
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: L('Yes'),
|
||||
@@ -16,7 +19,7 @@
|
||||
{
|
||||
if (result === true)
|
||||
{
|
||||
Grocy.Api.Get('delete-object/batteries/' + $(e.currentTarget).attr('data-battery-id'),
|
||||
Grocy.Api.Get('delete-object/batteries/' + objectId,
|
||||
function(result)
|
||||
{
|
||||
window.location.href = U('/batteries');
|
||||
|
@@ -1,6 +1,6 @@
|
||||
Grocy.Components.HabitCard = { };
|
||||
|
||||
Grocy.Components.HabitCard.Refresh = function (habitId)
|
||||
Grocy.Components.HabitCard.Refresh = function(habitId)
|
||||
{
|
||||
Grocy.Api.Get('habits/get-habit-details/' + habitId,
|
||||
function(habitDetails)
|
||||
|
@@ -60,7 +60,7 @@ $('#product_id').on('change', function(e)
|
||||
$('#product_id_text_input').addClass('has-error');
|
||||
$('#product_id_text_input').parent('.input-group').addClass('has-error');
|
||||
$('#product_id_text_input').closest('.form-group').addClass('has-error');
|
||||
$('#product-error').text('This product is not in stock.');
|
||||
$('#product-error').text(L('This product is not in stock'));
|
||||
$('#product-error').show();
|
||||
$('#product_id_text_input').focus();
|
||||
}
|
||||
|
@@ -41,7 +41,7 @@ $('.input-group-habit-period-type').on('change', function(e)
|
||||
|
||||
if (periodType === 'dynamic-regular')
|
||||
{
|
||||
$('#habit-period-type-info').text('This means it is estimated that a new "execution" of this habit is tracked ' + periodDays.toString() + ' days after the last was tracked.');
|
||||
$('#habit-period-type-info').text(L('This means it is estimated that a new execution of this habit is tracked #1 days after the last was tracked', periodDays.toString()));
|
||||
$('#habit-period-type-info').show();
|
||||
}
|
||||
else
|
||||
|
@@ -1,7 +1,10 @@
|
||||
$(document).on('click', '.habit-delete-button', function(e)
|
||||
{
|
||||
var objectName = $(e.currentTarget).attr('data-habit-name');
|
||||
var objectId = $(e.currentTarget).attr('data-habit-id');
|
||||
|
||||
bootbox.confirm({
|
||||
message: L('Are you sure to delete habit "#1"?', $(e.currentTarget).attr('data-habit-name')),
|
||||
message: L('Are you sure to delete habit "#1"?', objectName),
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: L('Yes'),
|
||||
@@ -16,7 +19,7 @@
|
||||
{
|
||||
if (result === true)
|
||||
{
|
||||
Grocy.Api.Get('delete-object/habits/' + $(e.currentTarget).attr('data-habit-id'),
|
||||
Grocy.Api.Get('delete-object/habits/' + objectId,
|
||||
function(result)
|
||||
{
|
||||
window.location.href = U('/habits');
|
||||
|
@@ -291,14 +291,14 @@ $('#new_amount').on('change', function(e)
|
||||
if (newAmount > productStockAmount)
|
||||
{
|
||||
var amountToAdd = newAmount - productDetails.stock_amount;
|
||||
$('#inventory-change-info').text('This means ' + amountToAdd.toString() + ' ' + productDetails.quantity_unit_stock.name + ' will be added to stock');
|
||||
$('#inventory-change-info').text(L('This means #1 will be added to stock', amountToAdd.toString() + ' ' + productDetails.quantity_unit_stock.name));
|
||||
$('#inventory-change-info').show();
|
||||
$('#best_before_date').attr('required', 'required');
|
||||
}
|
||||
else if (newAmount < productStockAmount)
|
||||
{
|
||||
var amountToRemove = productStockAmount - newAmount;
|
||||
$('#inventory-change-info').text('This means ' + amountToRemove.toString() + ' ' + productDetails.quantity_unit_stock.name + ' will be removed from stock');
|
||||
$('#inventory-change-info').text(L('This means #1 will be removed from stock', amountToRemove.toString() + ' ' + productDetails.quantity_unit_stock.name));
|
||||
$('#inventory-change-info').show();
|
||||
$('#best_before_date').removeAttr('required');
|
||||
}
|
||||
|
@@ -1,7 +1,10 @@
|
||||
$(document).on('click', '.location-delete-button', function(e)
|
||||
{
|
||||
var objectName = $(e.currentTarget).attr('data-location-name');
|
||||
var objectId = $(e.currentTarget).attr('data-location-id');
|
||||
|
||||
bootbox.confirm({
|
||||
message: L('Are you sure to delete location "#1"?', $(e.currentTarget).attr('data-location-name')),
|
||||
message: L('Are you sure to delete location "#1"?', objectName),
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: L('Yes'),
|
||||
@@ -16,7 +19,7 @@
|
||||
{
|
||||
if (result === true)
|
||||
{
|
||||
Grocy.Api.Get('delete-object/locations/' + $(e.currentTarget).attr('data-location-id'),
|
||||
Grocy.Api.Get('delete-object/locations/' + objectId,
|
||||
function(result)
|
||||
{
|
||||
window.location.href = U('/locations');
|
||||
|
50
public/viewjs/manageapikeys.js
Normal file
50
public/viewjs/manageapikeys.js
Normal file
@@ -0,0 +1,50 @@
|
||||
$(document).on('click', '.apikey-delete-button', function(e)
|
||||
{
|
||||
var objectName = $(e.currentTarget).attr('data-apikey-apikey');
|
||||
var objectId = $(e.currentTarget).attr('data-apikey-id');
|
||||
|
||||
bootbox.confirm({
|
||||
message: L('Are you sure to delete API key "#1"?', objectName),
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: L('Yes'),
|
||||
className: 'btn-success'
|
||||
},
|
||||
cancel: {
|
||||
label: L('No'),
|
||||
className: 'btn-danger'
|
||||
}
|
||||
},
|
||||
callback: function(result)
|
||||
{
|
||||
if (result === true)
|
||||
{
|
||||
Grocy.Api.Get('delete-object/api_keys/' + objectId,
|
||||
function(result)
|
||||
{
|
||||
window.location.href = U('/manageapikeys');
|
||||
},
|
||||
function(xhr)
|
||||
{
|
||||
console.error(xhr);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#apikeys-table').DataTable({
|
||||
'pageLength': 50,
|
||||
'order': [[4, 'desc']],
|
||||
'columnDefs': [
|
||||
{ 'orderable': false, 'targets': 0 }
|
||||
],
|
||||
'language': JSON.parse(L('datatables_localization'))
|
||||
});
|
||||
|
||||
var createdApiKeyId = GetUriParam('CreatedApiKeyId');
|
||||
if (createdApiKeyId !== undefined)
|
||||
{
|
||||
$('#apiKeyRow_' + createdApiKeyId).effect('highlight', { }, 3000);
|
||||
}
|
26
public/viewjs/openapiui.js
Normal file
26
public/viewjs/openapiui.js
Normal file
@@ -0,0 +1,26 @@
|
||||
function HideTopbarPlugin()
|
||||
{
|
||||
return {
|
||||
components: {
|
||||
Topbar: function () { return null }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const swaggerUi = SwaggerUIBundle({
|
||||
url: Grocy.OpenApi.SpecUrl,
|
||||
dom_id: '#swagger-ui',
|
||||
deepLinking: true,
|
||||
presets: [
|
||||
SwaggerUIBundle.presets.apis,
|
||||
SwaggerUIStandalonePreset
|
||||
],
|
||||
plugins: [
|
||||
SwaggerUIBundle.plugins.DownloadUrl,
|
||||
HideTopbarPlugin
|
||||
],
|
||||
layout: 'StandaloneLayout',
|
||||
docExpansion: "list"
|
||||
});
|
||||
|
||||
window.ui = swaggerUi;
|
@@ -1,7 +1,10 @@
|
||||
$(document).on('click', '.product-delete-button', function(e)
|
||||
{
|
||||
var objectName = $(e.currentTarget).attr('data-product-name');
|
||||
var objectId = $(e.currentTarget).attr('data-product-id');
|
||||
|
||||
bootbox.confirm({
|
||||
message: L('Are you sure to delete product "#1"?', $(e.currentTarget).attr('data-product-name')),
|
||||
message: L('Are you sure to delete product "#1"?', objectName),
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: L('Yes'),
|
||||
@@ -16,7 +19,7 @@
|
||||
{
|
||||
if (result === true)
|
||||
{
|
||||
Grocy.Api.Get('delete-object/products/' + $(e.currentTarget).attr('data-product-id'),
|
||||
Grocy.Api.Get('delete-object/products/' + objectId,
|
||||
function(result)
|
||||
{
|
||||
window.location.href = U('/products');
|
||||
|
@@ -1,7 +1,10 @@
|
||||
$(document).on('click', '.quantityunit-delete-button', function(e)
|
||||
{
|
||||
var objectName = $(e.currentTarget).attr('data-quantityunit-name');
|
||||
var objectId = $(e.currentTarget).attr('data-quantityunit-id');
|
||||
|
||||
bootbox.confirm({
|
||||
message: L('Are you sure to delete quantity unit "#1"?', $(e.currentTarget).attr('data-quantityunit-name')),
|
||||
message: L('Are you sure to delete quantity unit "#1"?', objectName),
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Yes',
|
||||
@@ -16,7 +19,7 @@
|
||||
{
|
||||
if (result === true)
|
||||
{
|
||||
Grocy.Api.Get('delete-object/quantity_units/' + $(e.currentTarget).attr('data-quantityunit-id'),
|
||||
Grocy.Api.Get('delete-object/quantity_units/' + objectId,
|
||||
function(result)
|
||||
{
|
||||
window.location.href = U('/quantityunits');
|
||||
|
82
routes.php
82
routes.php
@@ -2,51 +2,62 @@
|
||||
|
||||
use \Grocy\Middleware\JsonMiddleware;
|
||||
use \Grocy\Middleware\CliMiddleware;
|
||||
use \Grocy\Middleware\SessionAuthMiddleware;
|
||||
use \Grocy\Middleware\ApiKeyAuthMiddleware;
|
||||
use \Tuupola\Middleware\CorsMiddleware;
|
||||
|
||||
// Base route
|
||||
$app->get('/', 'Grocy\Controllers\LoginController:Root')->setName('root');
|
||||
$app->group('', function()
|
||||
{
|
||||
// Base route
|
||||
$this->get('/', 'LoginControllerInstance:Root')->setName('root');
|
||||
|
||||
// Login routes
|
||||
$app->get('/login', 'Grocy\Controllers\LoginController:LoginPage')->setName('login');
|
||||
$app->post('/login', 'Grocy\Controllers\LoginController:ProcessLogin')->setName('login');
|
||||
$app->get('/logout', 'Grocy\Controllers\LoginController:Logout');
|
||||
// Login routes
|
||||
$this->get('/login', 'LoginControllerInstance:LoginPage')->setName('login');
|
||||
$this->post('/login', 'LoginControllerInstance:ProcessLogin')->setName('login');
|
||||
$this->get('/logout', 'LoginControllerInstance:Logout');
|
||||
|
||||
// Stock routes
|
||||
$app->get('/stockoverview', 'Grocy\Controllers\StockController:Overview');
|
||||
$app->get('/purchase', 'Grocy\Controllers\StockController:Purchase');
|
||||
$app->get('/consume', 'Grocy\Controllers\StockController:Consume');
|
||||
$app->get('/inventory', 'Grocy\Controllers\StockController:Inventory');
|
||||
// Stock routes
|
||||
$this->get('/stockoverview', 'Grocy\Controllers\StockController:Overview');
|
||||
$this->get('/purchase', 'Grocy\Controllers\StockController:Purchase');
|
||||
$this->get('/consume', 'Grocy\Controllers\StockController:Consume');
|
||||
$this->get('/inventory', 'Grocy\Controllers\StockController:Inventory');
|
||||
|
||||
$app->get('/products', 'Grocy\Controllers\StockController:ProductsList');
|
||||
$app->get('/product/{productId}', 'Grocy\Controllers\StockController:ProductEditForm');
|
||||
$this->get('/products', 'Grocy\Controllers\StockController:ProductsList');
|
||||
$this->get('/product/{productId}', 'Grocy\Controllers\StockController:ProductEditForm');
|
||||
|
||||
$app->get('/locations', 'Grocy\Controllers\StockController:LocationsList');
|
||||
$app->get('/location/{locationId}', 'Grocy\Controllers\StockController:LocationEditForm');
|
||||
$this->get('/locations', 'Grocy\Controllers\StockController:LocationsList');
|
||||
$this->get('/location/{locationId}', 'Grocy\Controllers\StockController:LocationEditForm');
|
||||
|
||||
$app->get('/quantityunits', 'Grocy\Controllers\StockController:QuantityUnitsList');
|
||||
$app->get('/quantityunit/{quantityunitId}', 'Grocy\Controllers\StockController:QuantityUnitEditForm');
|
||||
$this->get('/quantityunits', 'Grocy\Controllers\StockController:QuantityUnitsList');
|
||||
$this->get('/quantityunit/{quantityunitId}', 'Grocy\Controllers\StockController:QuantityUnitEditForm');
|
||||
|
||||
$app->get('/shoppinglist', 'Grocy\Controllers\StockController:ShoppingList');
|
||||
$app->get('/shoppinglistitem/{itemId}', 'Grocy\Controllers\StockController:ShoppingListItemEditForm');
|
||||
$this->get('/shoppinglist', 'Grocy\Controllers\StockController:ShoppingList');
|
||||
$this->get('/shoppinglistitem/{itemId}', 'Grocy\Controllers\StockController:ShoppingListItemEditForm');
|
||||
|
||||
// Habit routes
|
||||
$this->get('/habitsoverview', 'Grocy\Controllers\HabitsController:Overview');
|
||||
$this->get('/habittracking', 'Grocy\Controllers\HabitsController:TrackHabitExecution');
|
||||
|
||||
// Habit routes
|
||||
$app->get('/habitsoverview', 'Grocy\Controllers\HabitsController:Overview');
|
||||
$app->get('/habittracking', 'Grocy\Controllers\HabitsController:TrackHabitExecution');
|
||||
$this->get('/habits', 'Grocy\Controllers\HabitsController:HabitsList');
|
||||
$this->get('/habit/{habitId}', 'Grocy\Controllers\HabitsController:HabitEditForm');
|
||||
|
||||
$app->get('/habits', 'Grocy\Controllers\HabitsController:HabitsList');
|
||||
$app->get('/habit/{habitId}', 'Grocy\Controllers\HabitsController:HabitEditForm');
|
||||
// Batterry routes
|
||||
$this->get('/batteriesoverview', 'Grocy\Controllers\BatteriesController:Overview');
|
||||
$this->get('/batterytracking', 'Grocy\Controllers\BatteriesController:TrackChargeCycle');
|
||||
|
||||
// Batterry routes
|
||||
$app->get('/batteriesoverview', 'Grocy\Controllers\BatteriesController:Overview');
|
||||
$app->get('/batterytracking', 'Grocy\Controllers\BatteriesController:TrackChargeCycle');
|
||||
|
||||
$app->get('/batteries', 'Grocy\Controllers\BatteriesController:BatteriesList');
|
||||
$app->get('/battery/{batteryId}', 'Grocy\Controllers\BatteriesController:BatteryEditForm');
|
||||
$this->get('/batteries', 'Grocy\Controllers\BatteriesController:BatteriesList');
|
||||
$this->get('/battery/{batteryId}', 'Grocy\Controllers\BatteriesController:BatteryEditForm');
|
||||
|
||||
// Other routes
|
||||
$this->get('/api', 'Grocy\Controllers\OpenApiController:DocumentationUi');
|
||||
$this->get('/manageapikeys', 'Grocy\Controllers\OpenApiController:ApiKeysList');
|
||||
$this->get('/manageapikeys/new', 'Grocy\Controllers\OpenApiController:CreateNewApiKey');
|
||||
})->add(new SessionAuthMiddleware($appContainer, $appContainer->LoginControllerInstance->GetSessionCookieName()));
|
||||
|
||||
$app->group('/api', function()
|
||||
{
|
||||
$this->get('/get-openapi-specification', 'Grocy\Controllers\OpenApiController:DocumentationSpec');
|
||||
|
||||
$this->get('/get-objects/{entity}', 'Grocy\Controllers\GenericEntityApiController:GetObjects');
|
||||
$this->get('/get-object/{entity}/{objectId}', 'Grocy\Controllers\GenericEntityApiController:GetObject');
|
||||
$this->post('/add-object/{entity}', 'Grocy\Controllers\GenericEntityApiController:AddObject');
|
||||
@@ -65,7 +76,16 @@ $app->group('/api', function()
|
||||
|
||||
$this->get('/batteries/track-charge-cycle/{batteryId}', 'Grocy\Controllers\BatteriesApiController:TrackChargeCycle');
|
||||
$this->get('/batteries/get-battery-details/{batteryId}', 'Grocy\Controllers\BatteriesApiController:BatteryDetails');
|
||||
})->add(JsonMiddleware::class);
|
||||
})->add(new ApiKeyAuthMiddleware($appContainer, $appContainer->LoginControllerInstance->GetSessionCookieName(), $appContainer->ApiKeyHeaderName))
|
||||
->add(JsonMiddleware::class)
|
||||
->add(new CorsMiddleware([
|
||||
'origin' => ["*"],
|
||||
'methods' => ["GET", "POST"],
|
||||
'headers.allow' => [ $appContainer->ApiKeyHeaderName ],
|
||||
'headers.expose' => [ ],
|
||||
'credentials' => false,
|
||||
'cache' => 0,
|
||||
]));
|
||||
|
||||
$app->group('/cli', function()
|
||||
{
|
||||
|
64
services/ApiKeyService.php
Normal file
64
services/ApiKeyService.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Grocy\Services;
|
||||
|
||||
class ApiKeyService extends BaseService
|
||||
{
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function IsValidApiKey($apiKey)
|
||||
{
|
||||
if ($apiKey === null || empty($apiKey))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$apiKeyRow = $this->Database->api_keys()->where('api_key = :1 AND expires > :2', $apiKey, date('Y-m-d H:i:s', time()))->fetch();
|
||||
if ($apiKeyRow !== null)
|
||||
{
|
||||
$apiKeyRow->update(array(
|
||||
'last_used' => date('Y-m-d H:i:s', time())
|
||||
));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function CreateApiKey()
|
||||
{
|
||||
$newApiKey = $this->GenerateApiKey();
|
||||
|
||||
$apiKeyRow = $this->Database->api_keys()->createRow(array(
|
||||
'api_key' => $newApiKey,
|
||||
'expires' => '2999-12-31 23:59:59' // Default is that API keys expire never
|
||||
));
|
||||
$apiKeyRow->save();
|
||||
|
||||
return $newApiKey;
|
||||
}
|
||||
|
||||
public function RemoveApiKey($apiKey)
|
||||
{
|
||||
$this->Database->api_keys()->where('api_key', $apiKey)->delete();
|
||||
}
|
||||
|
||||
public function GetApiKeyId($apiKey)
|
||||
{
|
||||
$apiKey = $this->Database->api_keys()->where('api_key', $apiKey)->fetch();
|
||||
return $apiKey->id;
|
||||
}
|
||||
|
||||
private function GenerateApiKey()
|
||||
{
|
||||
return RandomString(50);
|
||||
}
|
||||
}
|
@@ -15,7 +15,18 @@ class SessionService extends BaseService
|
||||
}
|
||||
else
|
||||
{
|
||||
return $this->Database->sessions()->where('session_key = :1 AND expires > :2', $sessionKey, time())->count() === 1;
|
||||
$sessionRow = $this->Database->sessions()->where('session_key = :1 AND expires > :2', $sessionKey, date('Y-m-d H:i:s', time()))->fetch();
|
||||
if ($sessionRow !== null)
|
||||
{
|
||||
$sessionRow->update(array(
|
||||
'last_used' => date('Y-m-d H:i:s', time())
|
||||
));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,11 +35,11 @@ class SessionService extends BaseService
|
||||
*/
|
||||
public function CreateSession()
|
||||
{
|
||||
$newSessionKey = uniqid() . uniqid() . uniqid();
|
||||
$newSessionKey = $this->GenerateSessionKey();
|
||||
|
||||
$sessionRow = $this->Database->sessions()->createRow(array(
|
||||
'session_key' => $newSessionKey,
|
||||
'expires' => time() + 2592000 //30 days
|
||||
'expires' => date('Y-m-d H:i:s', time() + 2592000) // Default is that sessions expire in 30 days
|
||||
));
|
||||
$sessionRow->save();
|
||||
|
||||
@@ -39,4 +50,9 @@ class SessionService extends BaseService
|
||||
{
|
||||
$this->Database->sessions()->where('session_key', $sessionKey)->delete();
|
||||
}
|
||||
|
||||
private function GenerateSessionKey()
|
||||
{
|
||||
return RandomString(50);
|
||||
}
|
||||
}
|
||||
|
@@ -1 +1 @@
|
||||
1.8.2
|
||||
1.9.0
|
||||
|
@@ -9,7 +9,7 @@
|
||||
|
||||
<h1 class="page-header">
|
||||
@yield('title')
|
||||
<a class="btn btn-default" href="/battery/new" role="button">
|
||||
<a class="btn btn-default" href="{{ $U('/battery/new') }}" role="button">
|
||||
<i class="fa fa-plus"></i> {{ $L('Add') }}
|
||||
</a>
|
||||
</h1>
|
||||
@@ -28,7 +28,7 @@
|
||||
@foreach($batteries as $battery)
|
||||
<tr>
|
||||
<td class="fit-content">
|
||||
<a class="btn btn-info" href="/battery/{{ $battery->id }}" role="button">
|
||||
<a class="btn btn-info" href="{{ $U('/battery/') }}{{ $battery->id }}" role="button">
|
||||
<i class="fa fa-pencil"></i>
|
||||
</a>
|
||||
<a class="btn btn-danger battery-delete-button" href="#" role="button" data-battery-id="{{ $battery->id }}" data-battery-name="{{ $battery->name }}">
|
||||
|
@@ -9,7 +9,7 @@
|
||||
|
||||
<h1 class="page-header">
|
||||
@yield('title')
|
||||
<a class="btn btn-default" href="/habit/new" role="button">
|
||||
<a class="btn btn-default" href="{{ $U('/habit/new') }}" role="button">
|
||||
<i class="fa fa-plus"></i> {{ $L('Add') }}
|
||||
</a>
|
||||
</h1>
|
||||
@@ -29,7 +29,7 @@
|
||||
@foreach($habits as $habit)
|
||||
<tr>
|
||||
<td class="fit-content">
|
||||
<a class="btn btn-info" href="/habit/{{ $habit->id }}" role="button">
|
||||
<a class="btn btn-info" href="{{ $U('/habit/') }}{{ $habit->id }}" role="button">
|
||||
<i class="fa fa-pencil"></i>
|
||||
</a>
|
||||
<a class="btn btn-danger habit-delete-button" href="#" role="button" data-habit-id="{{ $habit->id }}" data-habit-name="{{ $habit->name }}">
|
||||
|
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="{{ CULTURE }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||
@@ -9,7 +9,7 @@
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
|
||||
<meta name="author" content="Bernd Bestel (bernd@berrnd.de)">
|
||||
<link rel="icon" type="image/png" sizes="200x200" href="/img/grocy.png">
|
||||
<link rel="icon" type="image/png" sizes="200x200" href="{{ $U('/img/grocy.png?v=') }}{{ $version }}">
|
||||
|
||||
<title>@yield('title') | grocy</title>
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
<link href="{{ $U('/bower_components/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css?v=') }}{{ $version }}" rel="stylesheet">
|
||||
<link href="{{ $U('/components_unmanaged/noto-sans-v6-latin/noto-sans-v6-latin.css?v=') }}{{ $version }}" rel="stylesheet">
|
||||
<link href="{{ $U('/css/grocy.css?v=') }}{{ $version }}" rel="stylesheet">
|
||||
@stack('pageStyles')
|
||||
|
||||
<script>
|
||||
var Grocy = { };
|
||||
@@ -48,8 +49,20 @@
|
||||
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li>
|
||||
<a class="discrete-link logout-button" href="{{ $U('/logout') }}"><i class="fa fa-sign-out fa-fw"></i> {{ $L('Logout') }}</a>
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">{{ HTTP_USER }} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a class="discrete-link logout-button" href="{{ $U('/logout') }}"><i class="fa fa-sign-out fa-fw"></i> {{ $L('Logout') }}</a>
|
||||
</li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li>
|
||||
<a class="discrete-link" href="{{ $U('/manageapikeys') }}"><i class="fa fa-handshake-o fa-fw"></i> {{ $L('Manage API keys') }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="discrete-link" target="_blank" href="{{ $U('/api') }}"><i class="fa fa-book"></i> {{ $L('REST API & data model documentation') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -110,8 +123,20 @@
|
||||
</ul>
|
||||
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li>
|
||||
<a class="discrete-link logout-button" href="{{ $U('/logout') }}"><i class="fa fa-sign-out fa-fw"></i> {{ $L('Logout') }}</a>
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">{{ HTTP_USER }} <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a class="discrete-link logout-button" href="{{ $U('/logout') }}"><i class="fa fa-sign-out fa-fw"></i> {{ $L('Logout') }}</a>
|
||||
</li>
|
||||
<li role="separator" class="divider"></li>
|
||||
<li>
|
||||
<a class="discrete-link" href="{{ $U('/manageapikeys') }}"><i class="fa fa-handshake-o fa-fw"></i> {{ $L('Manage API keys') }}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="discrete-link" target="_blank" href="{{ $U('/api') }}"><i class="fa fa-book"></i> {{ $L('REST API & data model documentation') }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -237,6 +262,7 @@
|
||||
<script src="{{ $U('/js/extensions.js?v=') }}{{ $version }}"></script>
|
||||
<script src="{{ $U('/js/grocy.js?v=') }}{{ $version }}"></script>
|
||||
<script src="{{ $U('/viewjs') }}/@yield('viewJsName').js?v={{ $version }}"></script>
|
||||
@stack('pageScripts')
|
||||
@stack('componentScripts')
|
||||
|
||||
@if(file_exists(__DIR__ . '/../../data/add_before_end_body.html'))
|
||||
|
@@ -9,7 +9,7 @@
|
||||
|
||||
<h1 class="page-header">
|
||||
@yield('title')
|
||||
<a class="btn btn-default" href="/location/new" role="button">
|
||||
<a class="btn btn-default" href="{{ $U('/location/new') }}" role="button">
|
||||
<i class="fa fa-plus"></i> {{ $L('Add') }}
|
||||
</a>
|
||||
</h1>
|
||||
@@ -27,7 +27,7 @@
|
||||
@foreach($locations as $location)
|
||||
<tr>
|
||||
<td class="fit-content">
|
||||
<a class="btn btn-info" href="/location/{{ $location->id }}" role="button">
|
||||
<a class="btn btn-info" href="{{ $U('/location/') }}{{ $location->id }}" role="button">
|
||||
<i class="fa fa-pencil"></i>
|
||||
</a>
|
||||
<a class="btn btn-danger location-delete-button" href="#" role="button" data-location-id="{{ $location->id }}" data-location-name="{{ $location->name }}">
|
||||
|
64
views/manageapikeys.blade.php
Normal file
64
views/manageapikeys.blade.php
Normal file
@@ -0,0 +1,64 @@
|
||||
@extends('layout.default')
|
||||
|
||||
@section('title', $L('API keys'))
|
||||
@section('activeNav', '')
|
||||
@section('viewJsName', 'manageapikeys')
|
||||
|
||||
@push('pageScripts')
|
||||
<script src="{{ $U('/bower_components/jquery-ui/jquery-ui.min.js?v=') }}{{ $version }}"></script>
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2">
|
||||
|
||||
<h1 class="page-header">
|
||||
@yield('title')
|
||||
<a class="btn btn-default" href="{{ $U('/manageapikeys/new') }}" role="button">
|
||||
<i class="fa fa-plus"></i> {{ $L('Create new API key') }}
|
||||
</a>
|
||||
</h1>
|
||||
|
||||
<p class="lead"><a href="{{ $U('/api') }}" target="_blank">{{ $L('REST API & data model documentation') }}</a></p>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table id="apikeys-table" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>{{ $L('API key') }}</th>
|
||||
<th>{{ $L('Expires') }}</th>
|
||||
<th>{{ $L('Last used') }}</th>
|
||||
<th>{{ $L('Created') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($apiKeys as $apiKey)
|
||||
<tr id="apiKeyRow_{{ $apiKey->id }}">
|
||||
<td class="fit-content">
|
||||
<a class="btn btn-danger apikey-delete-button" href="#" role="button" data-apikey-id="{{ $apiKey->id }}" data-apikey-apikey="{{ $apiKey->api_key }}">
|
||||
<i class="fa fa-trash"></i>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
{{ $apiKey->api_key }}
|
||||
</td>
|
||||
<td>
|
||||
{{ $apiKey->expires }}
|
||||
<time class="timeago timeago-contextual" datetime="{{ $apiKey->expires }}"></time>
|
||||
</td>
|
||||
<td>
|
||||
@if(empty($apiKey->last_used)){{ $L('never') }}@else{{ $apiKey->last_used }}@endif
|
||||
<time class="timeago timeago-contextual" datetime="{{ $apiKey->last_used }}"></time>
|
||||
</td>
|
||||
<td>
|
||||
{{ $apiKey->row_created_timestamp }}
|
||||
<time class="timeago timeago-contextual" datetime="{{ $apiKey->row_created_timestamp }}"></time>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@stop
|
36
views/openapiui.blade.php
Normal file
36
views/openapiui.blade.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="x-ua-compatible" content="ie=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
||||
<meta name="robots" content="noindex,nofollow">
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
|
||||
<meta name="author" content="Bernd Bestel (bernd@berrnd.de)">
|
||||
<link rel="icon" type="image/png" sizes="200x200" href="{{ $U('/img/grocy.png?v=') }}{{ $version }}">
|
||||
|
||||
<title>{{ $L('REST API & data model documentation') }} | grocy</title>
|
||||
|
||||
<link href="{{ $U('/bower_components/swagger-ui/dist/swagger-ui.css?v=') }}{{ $version }}" rel="stylesheet">
|
||||
|
||||
<script>
|
||||
var Grocy = { };
|
||||
Grocy.OpenApi = { };
|
||||
Grocy.OpenApi.SpecUrl = '{{ $U('/api/get-openapi-specification') }}';
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="swagger-ui"></div>
|
||||
|
||||
<script src="{{ $U('/bower_components/swagger-ui/dist/swagger-ui-bundle.js?v=') }}{{ $version }}"></script>
|
||||
<script src="{{ $U('/bower_components/swagger-ui/dist/swagger-ui-standalone-preset.js?v=') }}{{ $version }}"></script>
|
||||
<script src="{{ $U('/viewjs') }}/openapiui.js?v={{ $version }}"></script>
|
||||
|
||||
@if(file_exists(__DIR__ . '/../../data/add_before_end_body.html'))
|
||||
@php include __DIR__ . '/../../data/add_before_end_body.html' @endphp
|
||||
@endif
|
||||
</body>
|
||||
</html>
|
@@ -9,7 +9,7 @@
|
||||
|
||||
<h1 class="page-header">
|
||||
@yield('title')
|
||||
<a class="btn btn-default" href="/product/new" role="button">
|
||||
<a class="btn btn-default" href="{{ $U('/product/new') }}" role="button">
|
||||
<i class="fa fa-plus"></i> {{ $L('Add') }}
|
||||
</a>
|
||||
</h1>
|
||||
@@ -32,7 +32,7 @@
|
||||
@foreach($products as $product)
|
||||
<tr>
|
||||
<td class="fit-content">
|
||||
<a class="btn btn-info" href="/product/{{ $product->id }}" role="button">
|
||||
<a class="btn btn-info" href="{{ $U('/product/') }}{{ $product->id }}" role="button">
|
||||
<i class="fa fa-pencil"></i>
|
||||
</a>
|
||||
<a class="btn btn-danger product-delete-button" href="#" role="button" data-product-id="{{ $product->id }}" data-product-name="{{ $product->name }}">
|
||||
|
@@ -20,7 +20,7 @@
|
||||
@endforeach
|
||||
</select>
|
||||
<div class="help-block with-errors"></div>
|
||||
<div id="flow-info-addbarcodetoselection" class="text-muted small hide"><strong><span id="addbarcodetoselection"></span></strong> will be added to the list of barcodes for the selected product on submit.</div>
|
||||
<div id="flow-info-addbarcodetoselection" class="text-muted small hide"><strong><span id="addbarcodetoselection"></span></strong> {{ $L('will be added to the list of barcodes for the selected product on submit') }}</div>
|
||||
</div>
|
||||
|
||||
@include('components.datepicker', array(
|
||||
|
@@ -9,7 +9,7 @@
|
||||
|
||||
<h1 class="page-header">
|
||||
@yield('title')
|
||||
<a class="btn btn-default" href="/quantityunit/new" role="button">
|
||||
<a class="btn btn-default" href="{{ $U('/quantityunit/new') }}" role="button">
|
||||
<i class="fa fa-plus"></i> Add
|
||||
</a>
|
||||
</h1>
|
||||
@@ -27,7 +27,7 @@
|
||||
@foreach($quantityunits as $quantityunit)
|
||||
<tr>
|
||||
<td class="fit-content">
|
||||
<a class="btn btn-info" href="/quantityunit/{{ $quantityunit->id }}" role="button">
|
||||
<a class="btn btn-info" href="{{ $U('/quantityunit/') }}{{ $quantityunit->id }}" role="button">
|
||||
<i class="fa fa-pencil"></i>
|
||||
</a>
|
||||
<a class="btn btn-danger quantityunit-delete-button" href="#" role="button" data-quantityunit-id="{{ $quantityunit->id }}" data-quantityunit-name="{{ $quantityunit->name }}">
|
||||
|
@@ -9,7 +9,7 @@
|
||||
|
||||
<h1 class="page-header">
|
||||
@yield('title')
|
||||
<a class="btn btn-default" href="/shoppinglistitem/new" role="button">
|
||||
<a class="btn btn-default" href="{{ $U('/shoppinglistitem/new') }}" role="button">
|
||||
<i class="fa fa-plus"></i> {{ $L('Add') }}
|
||||
</a>
|
||||
<a id="add-products-below-min-stock-amount" class="btn btn-info" href="#" role="button">
|
||||
@@ -30,7 +30,7 @@
|
||||
@foreach($listItems as $listItem)
|
||||
<tr class="@if($listItem->amount_autoadded > 0) info-bg @endif">
|
||||
<td class="fit-content">
|
||||
<a class="btn btn-info" href="/shoppinglistitem/{{ $listItem->id }}" role="button">
|
||||
<a class="btn btn-info" href="{{ $U('/shoppinglistitem/') }}{{ $listItem->id }}" role="button">
|
||||
<i class="fa fa-pencil"></i>
|
||||
</a>
|
||||
<a class="btn btn-danger shoppinglist-delete-button" href="#" role="button" data-shoppinglist-id="{{ $listItem->id }}">
|
||||
|
@@ -7,7 +7,7 @@
|
||||
@section('content')
|
||||
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2">
|
||||
|
||||
<h1 class="page-header">{{ $L('Stock overview') }} <span class="text-muded small">{{ $L('#1 products with #2 units in stock', count($currentStock), SumArrayValue($currentStock, 'amount')) }}</span></h1>
|
||||
<h1 class="page-header">{{ $L('Stock overview') }} <span class="text-muted small">{{ $L('#1 products with #2 units in stock', count($currentStock), SumArrayValue($currentStock, 'amount')) }}</span></h1>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
|
Reference in New Issue
Block a user