diff --git a/app/Controllers/ActorController.php b/app/Controllers/ActorController.php index a5965d2..df3d815 100644 --- a/app/Controllers/ActorController.php +++ b/app/Controllers/ActorController.php @@ -149,35 +149,135 @@ class ActorController extends Controller ]); } - // Handle image upload + // Handle image upload/download $thumbnailPath = $actor['thumbnail_path']; // Keep existing by default - if (!empty($uploadedFiles['thumbnail']) && $uploadedFiles['thumbnail']->getError() === UPLOAD_ERR_OK) { - $uploadedFile = $uploadedFiles['thumbnail']; + $imageSource = $data['image_source'] ?? 'upload'; - // Validate file type - $allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; - if (!in_array($uploadedFile->getClientMediaType(), $allowedTypes)) { - return $this->view->render($response, 'actor/edit.twig', [ - 'title' => 'Edit Actor', - 'actor' => $actor, - 'metadata' => $metadata, - 'error' => 'Invalid image type. Only JPEG, PNG, GIF, and WebP are allowed.' - ]); + if ($imageSource === 'upload') { + // Handle file upload + if (!empty($uploadedFiles['thumbnail']) && $uploadedFiles['thumbnail']->getError() === UPLOAD_ERR_OK) { + $uploadedFile = $uploadedFiles['thumbnail']; + + // Validate file type + $allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; + if (!in_array($uploadedFile->getClientMediaType(), $allowedTypes)) { + return $this->view->render($response, 'actor/edit.twig', [ + 'title' => 'Edit Actor', + 'actor' => $actor, + 'metadata' => $metadata, + 'error' => 'Invalid image type. Only JPEG, PNG, GIF, and WebP are allowed.' + ]); + } + + // Generate filename and move file + $extension = pathinfo($uploadedFile->getClientFilename(), PATHINFO_EXTENSION); + $filename = 'actor_' . $actorId . '_' . time() . '.' . $extension; + $uploadPath = __DIR__ . '/../../public/images/actors/' . $filename; + + // Create directory if it doesn't exist + $uploadDir = dirname($uploadPath); + if (!is_dir($uploadDir)) { + mkdir($uploadDir, 0755, true); + } + + $uploadedFile->moveTo($uploadPath); + $thumbnailPath = '/images/actors/' . $filename; } + } elseif ($imageSource === 'url') { + // Handle URL download + $imageUrl = trim($data['thumbnail_url'] ?? ''); + if (!empty($imageUrl)) { + // Validate URL + if (!filter_var($imageUrl, FILTER_VALIDATE_URL)) { + return $this->view->render($response, 'actor/edit.twig', [ + 'title' => 'Edit Actor', + 'actor' => $actor, + 'metadata' => $metadata, + 'error' => 'Invalid image URL provided.' + ]); + } - // Generate filename and move file - $extension = pathinfo($uploadedFile->getClientFilename(), PATHINFO_EXTENSION); - $filename = 'actor_' . $actorId . '_' . time() . '.' . $extension; - $uploadPath = __DIR__ . '/../../public/images/actors/' . $filename; + try { + // Download image from URL + $imageData = file_get_contents($imageUrl); + if ($imageData === false) { + return $this->view->render($response, 'actor/edit.twig', [ + 'title' => 'Edit Actor', + 'actor' => $actor, + 'metadata' => $metadata, + 'error' => 'Failed to download image from the provided URL.' + ]); + } - // Create directory if it doesn't exist - $uploadDir = dirname($uploadPath); - if (!is_dir($uploadDir)) { - mkdir($uploadDir, 0755, true); + // Get image info to validate type and determine extension + $imageInfo = getimagesizefromstring($imageData); + if (!$imageInfo) { + return $this->view->render($response, 'actor/edit.twig', [ + 'title' => 'Edit Actor', + 'actor' => $actor, + 'metadata' => $metadata, + 'error' => 'The URL does not point to a valid image.' + ]); + } + + // Validate MIME type + $allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; + if (!in_array($imageInfo['mime'], $allowedTypes)) { + return $this->view->render($response, 'actor/edit.twig', [ + 'title' => 'Edit Actor', + 'actor' => $actor, + 'metadata' => $metadata, + 'error' => 'Invalid image type. Only JPEG, PNG, GIF, and WebP are allowed.' + ]); + } + + // Determine extension from MIME type + $extension = ''; + switch ($imageInfo['mime']) { + case 'image/jpeg': + $extension = 'jpg'; + break; + case 'image/png': + $extension = 'png'; + break; + case 'image/gif': + $extension = 'gif'; + break; + case 'image/webp': + $extension = 'webp'; + break; + } + + // Generate filename and save file + $filename = 'actor_' . $actorId . '_' . time() . '.' . $extension; + $uploadPath = __DIR__ . '/../../public/images/actors/' . $filename; + + // Create directory if it doesn't exist + $uploadDir = dirname($uploadPath); + if (!is_dir($uploadDir)) { + mkdir($uploadDir, 0755, true); + } + + // Save the downloaded image + if (file_put_contents($uploadPath, $imageData) === false) { + return $this->view->render($response, 'actor/edit.twig', [ + 'title' => 'Edit Actor', + 'actor' => $actor, + 'metadata' => $metadata, + 'error' => 'Failed to save the downloaded image.' + ]); + } + + $thumbnailPath = '/images/actors/' . $filename; + } catch (Exception $e) { + return $this->view->render($response, 'actor/edit.twig', [ + 'title' => 'Edit Actor', + 'actor' => $actor, + 'metadata' => $metadata, + 'error' => 'Error downloading image: ' . $e->getMessage() + ]); + } } - - $uploadedFile->moveTo($uploadPath); - $thumbnailPath = '/images/actors/' . $filename; } // Prepare metadata @@ -246,13 +346,156 @@ class ActorController extends Controller 'metadata' => $metadata ]); } + public function fetchStashData(Request $request, Response $response, $args) + { + ini_set('memory_limit', '2G'); + + try { + // Parse JSON body for API requests + $rawBody = $request->getBody()->getContents(); + $data = json_decode($rawBody, true); + + if (json_last_error() !== JSON_ERROR_NONE) { + return $this->jsonResponse($response->withStatus(400), [ + 'error' => 'Invalid JSON in request body' + ]); + } + + $query = trim($data['query'] ?? ''); + + if (empty($query)) { + return $this->jsonResponse($response->withStatus(400), [ + 'error' => 'Missing required parameter: query' + ]); + } + + // Get Stash configuration from database + $stmt = $this->pdo->prepare('SELECT * FROM sources WHERE name = ?'); + $stmt->execute(['stash']); + $stashSource = $stmt->fetch(\PDO::FETCH_ASSOC); + + if (!$stashSource) { + return $this->jsonResponse($response->withStatus(500), [ + 'error' => 'Stash source not configured in database' + ]); + } + + $stashUrl = trim($stashSource['api_url'] ?? ''); + $stashApiKey = trim($stashSource['api_key'] ?? ''); + + if (empty($stashUrl)) { + return $this->jsonResponse($response->withStatus(500), [ + 'error' => 'Stash API URL not configured' + ]); + } + + // Create HTTP client for Stash API + $client = new \GuzzleHttp\Client([ + 'timeout' => 30, + 'verify' => false, + 'headers' => [ + 'User-Agent' => 'MediaCollector/1.0', + 'ApiKey' => $stashApiKey, + 'Content-Type' => 'application/json' + ] + ]); + + // Build GraphQL query to search for performers + $graphqlQuery = ' + query FindPerformers($filter: FindFilterType) { + findPerformers(filter: $filter) { + performers { + id + name + disambiguation + url + gender + birthdate + ethnicity + country + eye_color + height_cm + measurements + fake_tits + penis_length + circumcised + career_length + tattoos + piercings + alias_list + favorite + ignore_auto_tag + created_at + updated_at + details + death_date + hair_color + weight + image_path + scene_count + } + count + } + } + '; + + $variables = [ + 'filter' => [ + 'q' => $query, + 'per_page' => 10, // Limit results + 'sort' => 'name', + 'direction' => 'ASC' + ] + ]; + + // Make the API call + $apiResponse = $client->post(rtrim($stashUrl, '/') . '/graphql', [ + 'json' => [ + 'query' => $graphqlQuery, + 'variables' => $variables + ] + ]); + + $result = json_decode($apiResponse->getBody(), true); + + if (!isset($result['data']['findPerformers']['performers'])) { + return $this->jsonResponse($response, [ + 'performers' => [], + 'message' => 'No performers found' + ]); + } + + $performers = $result['data']['findPerformers']['performers']; + + return $this->jsonResponse($response, [ + 'performers' => $performers, + 'count' => count($performers), + 'stash_url' => $stashUrl + ]); + + } catch (\GuzzleHttp\Exception\RequestException $e) { + $errorMessage = 'Failed to connect to Stash server'; + if ($e->hasResponse()) { + $statusCode = $e->getResponse()->getStatusCode(); + $errorMessage .= ": HTTP {$statusCode}"; + } + return $this->jsonResponse($response->withStatus(500), [ + 'error' => $errorMessage + ]); + } catch (\Exception $e) { + return $this->jsonResponse($response->withStatus(500), [ + 'error' => 'Internal server error: ' . $e->getMessage() + ]); + } + } + public function index(Request $request, Response $response, $args) { $queryParams = $request->getQueryParams(); // Get pagination parameters $page = max(1, (int)($queryParams['page'] ?? 1)); - $perPage = max(12, min(100, (int)($queryParams['per_page'] ?? 24))); + $perPage = max(12, min(100, (int)($queryParams['per_page'] ?? 48))); // Get search parameters $search = trim($queryParams['search'] ?? ''); @@ -367,9 +610,51 @@ class ActorController extends Controller $orderBy = $sortMap[$sort] ?? 'total_media_count DESC, a.name ASC'; $sql .= " ORDER BY {$orderBy}"; - // Get total count for pagination - $countSql = str_replace('SELECT a.id, a.name, a.thumbnail_path,', 'SELECT COUNT(*) as count,', $sql); - $countSql = preg_replace('/ORDER BY.*$/', '', $countSql); + // Get total count for pagination - use a subquery to count the results + $countSql = " + SELECT COUNT(*) as count FROM ( + SELECT a.id, + COALESCE(adult_counts.adult_video_count, 0) as adult_video_count, + COALESCE(movie_counts.movie_count, 0) as movie_count, + COALESCE(tv_counts.tv_show_count, 0) as tv_show_count + FROM actors a + LEFT JOIN ( + SELECT actor_id, COUNT(DISTINCT adult_video_id) as adult_video_count + FROM actor_adult_video + GROUP BY actor_id + ) adult_counts ON a.id = adult_counts.actor_id + LEFT JOIN ( + SELECT actor_id, COUNT(DISTINCT movie_id) as movie_count + FROM actor_movie + GROUP BY actor_id + ) movie_counts ON a.id = movie_counts.actor_id + LEFT JOIN ( + SELECT actor_id, COUNT(DISTINCT tv_show_id) as tv_show_count + FROM ( + SELECT actor_id, tv_show_id FROM actor_tv_show + UNION + SELECT ate.actor_id, te.tv_show_id + FROM actor_tv_episode ate + JOIN tv_episodes te ON ate.tv_episode_id = te.id + ) combined_tv + GROUP BY actor_id + ) tv_counts ON a.id = tv_counts.actor_id + "; + + // Add search filter + if (!empty($whereClauses)) { + $countSql .= ' WHERE ' . implode(' AND ', $whereClauses); + } + + $countSql .= " GROUP BY a.id"; + + // Add HAVING clause for filters + if (!empty($havingClauses)) { + $countSql .= ' HAVING ' . implode(' AND ', $havingClauses); + } + + $countSql .= ") as filtered_actors"; + $countStmt = $this->pdo->prepare($countSql); foreach ($params as $key => $value) { $countStmt->bindValue($key, $value); diff --git a/public/index.php b/public/index.php index 74e61c2..5be08d4 100644 --- a/public/index.php +++ b/public/index.php @@ -57,7 +57,7 @@ $container->set('view', function () use ($container) { $_SERVER['HTTP_HOST'] ?? 'localhost' ); })); - + // Add media visibility function $twig->getEnvironment()->addFunction(new TwigFunction('is_media_type_visible', function ($mediaType) { return is_media_type_visible($mediaType); @@ -71,9 +71,6 @@ $container->set('view', function () use ($container) { // Handle common route patterns switch ($name) { case 'home': - $basePath = '/'; - break; - case 'dashboard.index': $basePath = '/'; break; case 'games.index': @@ -164,8 +161,192 @@ $container->set('view', function () use ($container) { return $authService->generateCSRFToken(); })); + $twig->getEnvironment()->addFunction(new TwigFunction('country_flag', function ($countryName) { + if (!$countryName) { + return ''; + } + + // Country name to ISO 3166-1 alpha-2 code mapping (most common countries) + $countryMap = [ + 'United States' => 'us', + 'USA' => 'us', + 'America' => 'us', + 'United Kingdom' => 'gb', + 'UK' => 'gb', + 'Britain' => 'gb', + 'England' => 'gb', + 'Germany' => 'de', + 'France' => 'fr', + 'Italy' => 'it', + 'Spain' => 'es', + 'Canada' => 'ca', + 'Australia' => 'au', + 'Japan' => 'jp', + 'China' => 'cn', + 'India' => 'in', + 'Brazil' => 'br', + 'Mexico' => 'mx', + 'Russia' => 'ru', + 'South Korea' => 'kr', + 'Netherlands' => 'nl', + 'Sweden' => 'se', + 'Norway' => 'no', + 'Denmark' => 'dk', + 'Finland' => 'fi', + 'Poland' => 'pl', + 'Czech Republic' => 'cz', + 'Hungary' => 'hu', + 'Romania' => 'ro', + 'Bulgaria' => 'bg', + 'Greece' => 'gr', + 'Portugal' => 'pt', + 'Belgium' => 'be', + 'Austria' => 'at', + 'Switzerland' => 'ch', + 'Ireland' => 'ie', + 'New Zealand' => 'nz', + 'South Africa' => 'za', + 'Argentina' => 'ar', + 'Chile' => 'cl', + 'Colombia' => 'co', + 'Peru' => 'pe', + 'Venezuela' => 've', + 'Ecuador' => 'ec', + 'Uruguay' => 'uy', + 'Paraguay' => 'py', + 'Bolivia' => 'bo', + 'Thailand' => 'th', + 'Vietnam' => 'vn', + 'Philippines' => 'ph', + 'Indonesia' => 'id', + 'Malaysia' => 'my', + 'Singapore' => 'sg', + 'Turkey' => 'tr', + 'Israel' => 'il', + 'Egypt' => 'eg', + 'Morocco' => 'ma', + 'Tunisia' => 'tn', + 'Algeria' => 'dz', + 'Saudi Arabia' => 'sa', + 'UAE' => 'ae', + 'United Arab Emirates' => 'ae', + 'Qatar' => 'qa', + 'Kuwait' => 'kw', + 'Bahrain' => 'bh', + 'Oman' => 'om', + 'Jordan' => 'jo', + 'Lebanon' => 'lb', + 'Syria' => 'sy', + 'Iraq' => 'iq', + 'Iran' => 'ir', + 'Pakistan' => 'pk', + 'Bangladesh' => 'bd', + 'Sri Lanka' => 'lk', + 'Nepal' => 'np', + 'Bhutan' => 'bt', + 'Maldives' => 'mv', + 'Afghanistan' => 'af', + 'Kazakhstan' => 'kz', + 'Uzbekistan' => 'uz', + 'Turkmenistan' => 'tm', + 'Kyrgyzstan' => 'kg', + 'Tajikistan' => 'tj', + 'Mongolia' => 'mn', + 'North Korea' => 'kp', + 'Taiwan' => 'tw', + 'Hong Kong' => 'hk', + 'Macau' => 'mo', + 'South Korea' => 'kr', + 'Cambodia' => 'kh', + 'Laos' => 'la', + 'Myanmar' => 'mm', + 'Brunei' => 'bn', + 'East Timor' => 'tl', + 'Papua New Guinea' => 'pg', + 'Fiji' => 'fj', + 'Solomon Islands' => 'sb', + 'Vanuatu' => 'vu', + 'Samoa' => 'ws', + 'Tonga' => 'to', + 'Tuvalu' => 'tv', + 'Kiribati' => 'ki', + 'Marshall Islands' => 'mh', + 'Micronesia' => 'fm', + 'Palau' => 'pw', + 'Nauru' => 'nr', + 'Cuba' => 'cu', + 'Jamaica' => 'jm', + 'Haiti' => 'ht', + 'Dominican Republic' => 'do', + 'Puerto Rico' => 'pr', + 'Bahamas' => 'bs', + 'Trinidad and Tobago' => 'tt', + 'Barbados' => 'bb', + 'Saint Lucia' => 'lc', + 'Saint Vincent and the Grenadines' => 'vc', + 'Grenada' => 'gd', + 'Antigua and Barbuda' => 'ag', + 'Saint Kitts and Nevis' => 'kn', + 'Dominica' => 'dm', + 'Saint Martin' => 'mf', + 'Guadeloupe' => 'gp', + 'Martinique' => 'mq', + 'French Guiana' => 'gf', + 'Suriname' => 'sr', + 'Guyana' => 'gy', + 'Belize' => 'bz', + 'Costa Rica' => 'cr', + 'Panama' => 'pa', + 'Nicaragua' => 'ni', + 'Honduras' => 'hn', + 'El Salvador' => 'sv', + 'Guatemala' => 'gt', + 'Greenland' => 'gl', + 'Iceland' => 'is', + 'Faroe Islands' => 'fo', + 'Ă…land Islands' => 'ax', + 'Guernsey' => 'gg', + 'Jersey' => 'je', + 'Isle of Man' => 'im', + 'Gibraltar' => 'gi', + 'Malta' => 'mt', + 'Cyprus' => 'cy', + 'Luxembourg' => 'lu', + 'Monaco' => 'mc', + 'Andorra' => 'ad', + 'San Marino' => 'sm', + 'Vatican City' => 'va', + 'Liechtenstein' => 'li', + 'Slovenia' => 'si', + 'Croatia' => 'hr', + 'Bosnia and Herzegovina' => 'ba', + 'Serbia' => 'rs', + 'Montenegro' => 'me', + 'Kosovo' => 'xk', + 'North Macedonia' => 'mk', + 'Albania' => 'al', + 'Moldova' => 'md', + 'Ukraine' => 'ua', + 'Belarus' => 'by', + 'Lithuania' => 'lt', + 'Latvia' => 'lv', + 'Estonia' => 'ee', + 'Slovakia' => 'sk', + 'Armenia' => 'am', + 'Azerbaijan' => 'az', + 'Georgia' => 'ge', + ]; + + $countryCode = strtolower($countryMap[$countryName] ?? ''); + + if ($countryCode) { + // Return Iconify flag icon HTML + return ''; + } + + return ''; + })); - $twig->getEnvironment()->addFilter(new TwigFilter('format_duration', function ($minutes) { if (!$minutes || $minutes == 0) { return '0m'; @@ -309,56 +490,56 @@ $app->add(TwigMiddleware::create($app, $twig)); // PHP DebugBar Setup (only in development) if ($_ENV['APP_DEBUG'] === 'true') { $debugbar = new \DebugBar\StandardDebugBar(); - + // Set up the debug bar renderer with the correct base URL $baseUrl = rtrim($app->getBasePath(), '/'); $debugbarRenderer = $debugbar->getJavascriptRenderer($baseUrl . '/phpdebugbar'); - + // Add DebugBar to Twig globals $twig->getEnvironment()->addGlobal('debugbarRenderer', $debugbarRenderer); - + // Add route to serve DebugBar assets $app->get('/phpdebugbar/{path:.*}', function ($request, $response, $args) use ($debugbar) { $debugbarRenderer = $debugbar->getJavascriptRenderer(); $path = $args['path']; - + // Serve CSS files if (preg_match('/\.css$/', $path)) { $content = file_get_contents($debugbarRenderer->getBasePath() . '/' . $path); $response->getBody()->write($content); return $response->withHeader('Content-Type', 'text/css'); } - + // Serve JS files if (preg_match('/\.js$/', $path)) { $content = file_get_contents($debugbarRenderer->getBasePath() . '/' . $path); $response->getBody()->write($content); return $response->withHeader('Content-Type', 'application/javascript'); } - + // Serve other assets (fonts, etc.) $content = @file_get_contents($debugbarRenderer->getBasePath() . '/' . $path); if ($content !== false) { $response->getBody()->write($content); return $response; } - + return $response->withStatus(404); }); - + // Add middleware to collect data $app->add(function ($request, $handler) use ($debugbar) { // Start timing the request $debugbar['time']->startMeasure('app', 'Application'); - + try { $response = $handler->handle($request); - + // Stop timing if it was started if ($debugbar['time']->hasStartedMeasure('app')) { $debugbar['time']->stopMeasure('app'); } - + return $response; } catch (\Exception $e) { // Make sure to stop timing even if an exception occurs diff --git a/public/test.php b/public/test.php new file mode 100644 index 0000000..6ee9a6a --- /dev/null +++ b/public/test.php @@ -0,0 +1,83 @@ + [ + 'per_page' => 100, + 'sort' => 'created_at', + 'direction' => 'DESC' + ] + ]; + + /* + 'json' => [ + 'query' => $query, + 'variables' => $variables + ]*/ + + +print_r (json_encode(str_replace("\r\n", '', $query) )); \ No newline at end of file diff --git a/resources/views/actor/edit.twig b/resources/views/actor/edit.twig index 1458645..7599197 100644 --- a/resources/views/actor/edit.twig +++ b/resources/views/actor/edit.twig @@ -43,8 +43,28 @@
- +
+ + + +
+
+ + + @@ -60,14 +80,63 @@ {% endif %} - +
+ +
+
+ + +
+
+ + +
+
+
+ + +

Supported formats: JPEG, PNG, GIF, WebP. Maximum file size: 5MB.

+ + +
@@ -277,4 +346,368 @@
+ + {% endblock %} diff --git a/resources/views/actor/index.twig b/resources/views/actor/index.twig index 686cabe..ca17527 100644 --- a/resources/views/actor/index.twig +++ b/resources/views/actor/index.twig @@ -11,13 +11,13 @@

Actors & Performers

{{ pagination.total_items }} performer{{ pagination.total_items != 1 ? 's' : '' }}

- + {{dump(pagination)}} - {% if pagination.total_pages > 0 %} + {% if pagination.total_pages > 1 %}
{% if pagination.has_prev %} - @@ -40,7 +40,7 @@ {% if pagination.has_next %} - Next @@ -265,7 +265,7 @@
{% if pagination.has_prev %} - @@ -286,7 +286,7 @@ {% set end_page = min(pagination.total_pages, pagination.current_page + 2) %} {% if start_page > 1 %} - 1 {% if start_page > 2 %} ... @@ -297,7 +297,7 @@ {% if page_num == pagination.current_page %} {{ page_num }} {% else %} - {{ page_num }} {% endif %} {% endfor %} @@ -306,13 +306,13 @@ {% if end_page < pagination.total_pages - 1 %} ... {% endif %} - {{ pagination.total_pages }} {% endif %} {% if pagination.has_next %} - Next diff --git a/resources/views/actor/show.twig b/resources/views/actor/show.twig index 733f6be..a5a6bd2 100644 --- a/resources/views/actor/show.twig +++ b/resources/views/actor/show.twig @@ -29,7 +29,10 @@ {% if metadata.nationality %}
Nationality - {{ metadata.nationality }} +
+ {{ country_flag(metadata.nationality) | raw }} + {{ metadata.nationality }} +
{% endif %} {% if metadata.death_date %} @@ -413,7 +416,7 @@
{% if scene.poster_url %} - {{ scene.title }} + {{ scene.title }} {% else %}
@@ -449,4 +452,4 @@ {% endif %}
-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/routes/api.php b/routes/api.php index f0d4339..6a0cdb1 100644 --- a/routes/api.php +++ b/routes/api.php @@ -19,6 +19,11 @@ $app->group('/api', function (RouteCollectorProxy $apiGroup) { $playniteGroup->post('/image/base64', 'App\Controllers\Api\PlayniteController:uploadImages')->setName('api.playnite.images'); }); + // Actor API endpoints + $apiGroup->group('/actors', function (RouteCollectorProxy $actorGroup) { + $actorGroup->post('/fetch-stash', 'App\Controllers\ActorController:fetchStashData')->setName('api.actors.fetch-stash'); + }); + // User authentication check (requires authentication) $apiGroup->get('/v1/users/me', 'App\Controllers\Api\AuthController:checkAuth')->setName('api.auth.check')->add('App\Http\Middleware\AuthMiddleware');