chengwl 3 달 전
부모
커밋
e3041eed24

+ 7 - 1
bootstrap/app.php

@@ -48,4 +48,10 @@ return Application::configure(basePath: dirname(__DIR__))
     })
     })
     ->withExceptions(function (Exceptions $exceptions) {
     ->withExceptions(function (Exceptions $exceptions) {
         //
         //
-    })->create();
+    })
+->withProviders([
+     \ApiPlatform\Laravel\ApiPlatformProvider::class,
+     \ApiPlatform\Laravel\ApiPlatformDeferredProvider::class,
+     \ApiPlatform\Laravel\Eloquent\ApiPlatformEventProvider::class,
+])
+->create();

+ 1 - 0
bootstrap/providers.php

@@ -44,4 +44,5 @@ return [
     Webkul\Tax\Providers\TaxServiceProvider::class,
     Webkul\Tax\Providers\TaxServiceProvider::class,
     Webkul\Theme\Providers\ThemeServiceProvider::class,
     Webkul\Theme\Providers\ThemeServiceProvider::class,
     Webkul\User\Providers\UserServiceProvider::class,
     Webkul\User\Providers\UserServiceProvider::class,
+    Webkul\BagistoApi\Providers\BagistoApiServiceProvider::class,
 ];
 ];

+ 15 - 8
composer.json

@@ -17,6 +17,8 @@
         "ext-pdo": "*",
         "ext-pdo": "*",
         "ext-pdo_mysql": "*",
         "ext-pdo_mysql": "*",
         "ext-tokenizer": "*",
         "ext-tokenizer": "*",
+        "api-platform/graphql": "v4.2.3",
+        "api-platform/laravel": "v4.1.25",
         "astrotomic/laravel-translatable": "^11.0.0",
         "astrotomic/laravel-translatable": "^11.0.0",
         "bagisto/image-cache": "dev-master",
         "bagisto/image-cache": "dev-master",
         "barryvdh/laravel-dompdf": "^2.0.0",
         "barryvdh/laravel-dompdf": "^2.0.0",
@@ -96,7 +98,9 @@
             "Webkul\\SocialShare\\": "packages/Webkul/SocialShare/src",
             "Webkul\\SocialShare\\": "packages/Webkul/SocialShare/src",
             "Webkul\\Tax\\": "packages/Webkul/Tax/src",
             "Webkul\\Tax\\": "packages/Webkul/Tax/src",
             "Webkul\\Theme\\": "packages/Webkul/Theme/src",
             "Webkul\\Theme\\": "packages/Webkul/Theme/src",
-            "Webkul\\User\\": "packages/Webkul/User/src"
+            "Webkul\\User\\": "packages/Webkul/User/src",
+            "Webkul\\BagistoApi\\": "packages/Webkul/BagistoApi/src",
+            "Webkul\\GraphQL\\": "packages/Webkul/GraphQL/src"
         }
         }
     },
     },
     "autoload-dev": {
     "autoload-dev": {
@@ -124,17 +128,20 @@
             "dont-discover": [
             "dont-discover": [
                 "intervention/image",
                 "intervention/image",
                 "laravel/socialite",
                 "laravel/socialite",
-                "shetabit/visitor"
+                "shetabit/visitor",
+                "api-platform/laravel"
             ]
             ]
         }
         }
     },
     },
-    "repositories": [{
-        "type": "path",
-        "url": "packages/*/*",
-        "options": {
-            "symlink": true
+    "repositories": [
+        {
+            "type": "path",
+            "url": "packages/*/*",
+            "options": {
+                "symlink": true
+            }
         }
         }
-    }],
+    ],
     "config": {
     "config": {
         "optimize-autoloader": true,
         "optimize-autoloader": true,
         "preferred-install": "dist",
         "preferred-install": "dist",

+ 230 - 0
config/api-platform.php

@@ -0,0 +1,230 @@
+<?php
+
+/*
+ * This file is part of the API Platform project.
+ *
+ * (c) Kévin Dunglas <dunglas@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+use ApiPlatform\Metadata\Operation\DashPathSegmentNameGenerator;
+use ApiPlatform\Metadata\UrlGeneratorInterface;
+use Illuminate\Auth\Access\AuthorizationException;
+use Illuminate\Auth\AuthenticationException;
+use Symfony\Component\Serializer\NameConverter\SnakeCaseToCamelCaseNameConverter;
+use Webkul\BagistoApi\Exception\InvalidInputException;
+use Webkul\BagistoApi\Exception\ValidationException;
+
+return [
+    'title'       => '',
+    'description' => '',
+    'version'     => '1.0.0',
+    'show_webby'  => true,
+
+    'routes' => [
+        'domain' => null,
+        // Global middleware applied to every API Platform routes
+        // HandleInvalidInputException: Catches validation errors and returns RFC 7807 format
+        // VerifyStorefrontKey: Validates X-STOREFRONT-KEY header and rate limiting for shop APIs
+        // BagistoApiDocumentationMiddleware: Handles custom /api index and documentation pages
+        // ForceApiJson: Ensures API responses have JSON content-type
+        // CacheResponse: Using custom ApiAwareResponseCache profile that:
+        // - Excludes API routes from caching (APIs need fresh data)
+        // - Caches shop pages for performance
+        // - Only caches HTML, not JSON responses
+        'middleware' => [
+            'Webkul\BagistoApi\Http\Middleware\HandleInvalidInputException',
+            'Webkul\BagistoApi\Http\Middleware\SecurityHeaders',
+            'Webkul\BagistoApi\Http\Middleware\LogApiRequests',
+            'Webkul\BagistoApi\Http\Middleware\SetLocaleChannel',
+            'Webkul\BagistoApi\Http\Middleware\VerifyStorefrontKey',
+            'Webkul\BagistoApi\Http\Middleware\BagistoApiDocumentationMiddleware',
+            'Webkul\BagistoApi\Http\Middleware\ForceApiJson',
+            'Spatie\ResponseCache\Middlewares\CacheResponse',
+        ],
+    ],
+
+    'resources' => [
+        base_path('packages/Webkul/BagistoApi/src/Models/'),
+    ],
+
+    'formats' => [
+        'json'=> ['application/json'],
+    ],
+
+    'patch_formats' => [
+        'json' => ['application/merge-patch+json'],
+    ],
+
+    'docs_formats' => [
+        'jsonopenapi' => ['application/vnd.openapi+json'],
+        'html'        => ['text/html'],
+    ],
+
+    'error_formats' => [
+        'jsonproblem' => ['application/problem+json'],
+    ],
+
+    'defaults' => [
+        'pagination_enabled'                => true,
+        'pagination_partial'                => false,
+        'pagination_client_enabled'         => false,
+        'pagination_client_items_per_page'  => false,
+        'pagination_client_partial'         => false,
+        'pagination_items_per_page'         => 10,
+        'pagination_maximum_items_per_page' => 50,
+        'route_prefix'                      => '/api',
+        'middleware'                        => [],
+    ],
+
+    'pagination' => [
+        'page_parameter_name'           => 'page',
+        'enabled_parameter_name'        => 'pagination',
+        'items_per_page_parameter_name' => 'itemsPerPage',
+        'partial_parameter_name'        => 'partial',
+    ],
+
+    'graphql' => [
+        'enabled'              => true,
+        'nesting_separator'    => '__',
+        'introspection'        => ['enabled' => true],
+        'max_query_complexity' => 400,
+        'max_query_depth'      => 20,
+        'graphiql'             => [
+            'enabled'           => true,
+            'default_query'     => null,
+            'default_variables' => null,
+        ],
+        'graphql_playground' => [
+            'enabled'           => true,
+            'default_query'     => null,
+            'default_variables' => null,
+        ],
+        // GraphQL middleware for authentication and rate limiting
+        'middleware' => [
+            'Webkul\BagistoApi\Http\Middleware\SetLocaleChannel',
+            'Webkul\BagistoApi\Http\Middleware\VerifyGraphQLStorefrontKey',
+        ],
+    ],
+
+    'graphiql' => [
+        'enabled' => true,
+    ],
+
+    'name_converter' => SnakeCaseToCamelCaseNameConverter::class,
+
+    'path_segment_name_generator' => DashPathSegmentNameGenerator::class,
+
+    'exception_to_status' => [
+        AuthenticationException::class => 401,
+        AuthorizationException::class  => 403,
+        ValidationException::class     => 400,
+        InvalidInputException::class   => 400,
+    ],
+
+    'swagger_ui' => [
+        'enabled' => true,
+        'apiKeys' => [
+            'api' => [
+                'name'   => 'Authorization',
+                'type'   => 'header',
+                'scheme' => 'bearer',
+            ],
+        ],
+    ],
+
+    'url_generation_strategy' => UrlGeneratorInterface::ABS_PATH,
+
+    'serializer' => [
+        'hydra_prefix'    => false,
+        'datetime_format' => 'Y-m-d\TH:i:sP',
+    ],
+
+    'cache' => 'redis',
+
+    'schema_cache' => [
+        'enabled' => true,
+        'store'   => 'redis',
+    ],
+
+    'security' => [
+        'sanctum' => true,
+    ],
+
+    'rate_limit' => [
+        'skip_localhost' => env('RATE_LIMIT_SKIP_LOCALHOST', true),
+        'auth'           => env('RATE_LIMIT_AUTH', 5),
+        'admin'          => env('RATE_LIMIT_ADMIN', 60),
+        'shop'           => env('RATE_LIMIT_SHOP', 100),
+        'graphql'        => env('RATE_LIMIT_GRAPHQL', 100),
+        'cache_driver'   => env('RATE_LIMIT_CACHE', 'redis'),
+        'cache_prefix'   => 'api:rate-limit:',
+    ],
+
+    'security_headers' => [
+        'enabled'     => true,
+        'force_https' => env('APP_FORCE_HTTPS', false),
+        'csp_header'  => "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'",
+    ],
+
+    'api_logging' => [
+        'enabled'            => env('API_LOG_ENABLED', true),
+        'log_sensitive_data' => env('API_LOG_SENSITIVE_DATA', false),
+        'exclude_paths'      => ['docs', 'graphiql', 'swagger-ui', 'docs.json'],
+        'channel'            => 'api',
+        'async'              => env('API_LOG_ASYNC', true),
+        'queue'              => env('API_LOG_QUEUE', 'api-logs'),
+    ],
+
+    'graphql_validation' => [
+        'max_depth'      => env('GRAPHQL_MAX_DEPTH', 10),
+        'max_complexity' => env('GRAPHQL_MAX_COMPLEXITY', 300),
+    ],
+
+    'request_limits' => [
+        'max_size_mb'          => env('MAX_REQUEST_SIZE', 10),
+        'max_pagination_limit' => env('MAX_PAGINATION_LIMIT', 100),
+    ],
+
+    'database' => [
+        'log_queries'          => env('DB_QUERY_LOG_ENABLED', false),
+        'slow_query_threshold' => env('DB_SLOW_QUERY_THRESHOLD', 1000),
+    ],
+
+    'caching' => [
+        'enable_security_cache' => env('API_SECURITY_CACHE', true),
+        'security_cache_ttl'    => env('API_SECURITY_CACHE_TTL', 3600),
+        'enable_response_cache' => env('API_RESPONSE_CACHE', true),
+        'response_cache_ttl'    => env('API_RESPONSE_CACHE_TTL', 3600),
+    ],
+
+    'http_cache' => [
+        'etag'                   => true,
+        'max_age'                => 3600,
+        'shared_max_age'         => null,
+        'vary'                   => null,
+        'public'                 => true,
+        'stale_while_revalidate' => 30,
+        'stale_if_error'         => null,
+        'invalidation'           => [
+            'urls'              => [],
+            'scoped_clients'    => [],
+            'max_header_length' => 7500,
+            'request_options'   => [],
+            'purger'            => ApiPlatform\HttpCache\SouinPurger::class,
+        ],
+    ],
+
+    'key_rotation_policy' => [
+        'enabled'               => true,
+        'expiration_months'     => env('API_KEY_EXPIRATION_MONTHS', 12),
+        'transition_days'       => env('API_KEY_TRANSITION_DAYS', 7),
+        'cleanup_days'          => env('API_KEY_CLEANUP_DAYS', 90),
+        'cache_ttl'             => env('API_KEY_CACHE_TTL', 3600),
+        'storefront_key_prefix' => env('STOREFRONT_KEY_PREFIX', 'pk_storefront_'),
+    ],
+];

+ 1 - 0
packages/Webkul/BagistoApi

@@ -0,0 +1 @@
+Subproject commit d9adbe5a0b6a0766887a5165eaeea6c87640a2c8

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 9 - 0
public/themes/admin/default/assets/images/admin-api.svg


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 3 - 0
public/themes/admin/default/assets/images/document.svg


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 9 - 0
public/themes/admin/default/assets/images/graph-QL.svg


BIN
public/themes/admin/default/assets/images/left-gradient.webp


BIN
public/themes/admin/default/assets/images/right-gradient.webp


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 9 - 0
public/themes/admin/default/assets/images/shop-api.svg


+ 6 - 0
public/themes/admin/default/assets/images/top-logo.svg

@@ -0,0 +1,6 @@
+<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect width="100" height="100" fill="white" style="fill:white;fill-opacity:1;"/>
+<rect x="17.3223" y="28.542" width="65.4582" height="56.2711" fill="#2149F3" style="fill:#2149F3;fill:color(display-p3 0.1294 0.2863 0.9529);fill-opacity:1;"/>
+<rect x="17.3223" y="84.8134" width="65.4582" height="9.18712" fill="black" style="fill:black;fill-opacity:1;"/>
+<path d="M70.7228 27.4745V42.735C70.7228 44.7271 69.1756 46.342 67.267 46.342C65.3584 46.342 63.8112 44.7271 63.8112 42.735V27.4745C63.8112 19.0464 57.2653 12.214 49.1905 12.214C41.1157 12.214 34.5698 19.0464 34.5698 27.4745V42.735C34.5698 44.7271 33.0226 46.342 31.114 46.342C29.2054 46.342 27.6582 44.7271 27.6582 42.735V27.4745C27.6582 15.0622 37.2985 5 49.1905 5C61.0825 5 70.7228 15.0622 70.7228 27.4745Z" fill="black" style="fill:black;fill-opacity:1;"/>
+</svg>