user(); })->middleware('auth:sanctum'); Route::post('/phpmyadmin/verify-token', function (Request $request) { $token = $request->input('token'); // Use Cache::get() which handles the prefix automatically $data = Cache::get('phpmyadmin_token_'.$token); if (! $data) { return response()->json(['error' => 'Invalid token'], 401); } // Delete token after use (single use) Cache::forget('phpmyadmin_token_'.$token); return response()->json($data); }); // Internal API for jabali-cache WordPress plugin Route::post('/internal/page-cache', function (Request $request) { // Only allow requests from localhost $clientIp = $request->ip(); if (! in_array($clientIp, ['127.0.0.1', '::1', 'localhost'])) { return response()->json(['error' => 'Forbidden'], 403); } $domain = $request->input('domain'); $enabled = $request->boolean('enabled'); $secret = $request->input('secret'); if (empty($domain)) { return response()->json(['error' => 'Domain is required'], 400); } // Validate secret matches what's in wp-config.php // The secret is the first 32 chars of AUTH_KEY from wp-config if (empty($secret) || strlen($secret) < 16) { return response()->json(['error' => 'Invalid secret'], 401); } // Find the user who owns this domain $user = \App\Models\User::whereHas('domains', function ($query) use ($domain) { $query->where('domain', $domain); })->first(); if (! $user) { return response()->json(['error' => 'Domain not found'], 404); } // Verify the secret by checking wp-config.php $wpConfigPath = "/home/{$user->username}/domains/{$domain}/public_html/wp-config.php"; if (! file_exists($wpConfigPath)) { return response()->json(['error' => 'WordPress not found'], 404); } $wpConfig = file_get_contents($wpConfigPath); if (preg_match("/define\s*\(\s*['\"]AUTH_KEY['\"]\s*,\s*['\"]([^'\"]+)['\"]\s*\)/", $wpConfig, $matches)) { $authKey = $matches[1]; $expectedSecret = substr(md5($authKey), 0, 32); if ($secret !== $expectedSecret) { return response()->json(['error' => 'Invalid secret'], 401); } } else { return response()->json(['error' => 'Cannot verify secret'], 401); } try { $agent = new AgentClient; if ($enabled) { $result = $agent->send('wp.page_cache_enable', [ 'username' => $user->username, 'domain' => $domain, ]); } else { $result = $agent->send('wp.page_cache_disable', [ 'username' => $user->username, 'domain' => $domain, ]); } return response()->json($result); } catch (\Exception $e) { return response()->json(['error' => $e->getMessage()], 500); } }); // Internal API for smart page cache purging (called by jabali-cache WordPress plugin) Route::post('/internal/page-cache-purge', function (Request $request) { // Only allow requests from localhost $clientIp = $request->ip(); if (! in_array($clientIp, ['127.0.0.1', '::1', 'localhost'])) { return response()->json(['error' => 'Forbidden'], 403); } $domain = $request->input('domain'); $paths = $request->input('paths', []); $purgeAll = $request->boolean('purge_all'); $secret = $request->input('secret'); if (empty($domain)) { return response()->json(['error' => 'Domain is required'], 400); } // Validate secret matches what's in wp-config.php if (empty($secret) || strlen($secret) < 16) { return response()->json(['error' => 'Invalid secret'], 401); } // Find the user who owns this domain $user = \App\Models\User::whereHas('domains', function ($query) use ($domain) { $query->where('domain', $domain); })->first(); if (! $user) { return response()->json(['error' => 'Domain not found'], 404); } // Verify the secret by checking wp-config.php $wpConfigPath = "/home/{$user->username}/domains/{$domain}/public_html/wp-config.php"; if (! file_exists($wpConfigPath)) { return response()->json(['error' => 'WordPress not found'], 404); } $wpConfig = file_get_contents($wpConfigPath); if (preg_match("/define\s*\(\s*['\"]AUTH_KEY['\"]\s*,\s*['\"]([^'\"]+)['\"]\s*\)/", $wpConfig, $matches)) { $authKey = $matches[1]; $expectedSecret = substr(md5($authKey), 0, 32); if ($secret !== $expectedSecret) { return response()->json(['error' => 'Invalid secret'], 401); } } else { return response()->json(['error' => 'Cannot verify secret'], 401); } try { $agent = new AgentClient; if ($purgeAll || empty($paths)) { // Purge entire domain cache $result = $agent->send('wp.page_cache_purge', [ 'domain' => $domain, ]); } else { // Purge specific paths $result = $agent->send('wp.page_cache_purge', [ 'domain' => $domain, 'paths' => $paths, ]); } return response()->json($result); } catch (\Exception $e) { return response()->json(['error' => $e->getMessage()], 500); } }); Route::post('/webhooks/git/{deployment}/{token}', GitWebhookController::class); Route::middleware(['auth:sanctum', 'abilities:automation']) ->prefix('automation') ->group(function () { Route::get('/users', [AutomationApiController::class, 'listUsers']); Route::post('/users', [AutomationApiController::class, 'createUser']); Route::post('/domains', [AutomationApiController::class, 'createDomain']); });