setAutoRoute(false); $routes->setDefaultNamespace('App\Controllers'); $routes->setDefaultController('Home'); $routes->setDefaultMethod('index'); // 404 override -> useful "Tool not found" page with popular tools. $routes->set404Override(static function () { return (new \App\Controllers\Errors())->notFound(); }); // Health probes (unfiltered, no session, no analytics). $routes->get('/health', 'Health::index'); $routes->get('/ready', 'Health::ready'); // --------------------------------------------------------------------- // SEO plumbing // --------------------------------------------------------------------- $routes->get('/robots.txt', 'Seo::robots'); $routes->get('/sitemap.xml', 'Seo::sitemapIndex'); $routes->get('/sitemap-tools-([0-9]+).xml', 'Seo::sitemapTools/$1'); $routes->get('/sitemap-guides.xml', 'Seo::sitemapGuides'); $routes->get('/sitemap-categories.xml', 'Seo::sitemapCategories'); $routes->get('/sitemap-pages.xml', 'Seo::sitemapPages'); $routes->get('/opensearch.xml', 'Seo::openSearch'); $routes->get('/manifest.webmanifest', 'Seo::manifest'); $routes->get('/service-worker.js', 'Seo::serviceWorker'); $routes->get('/og/(:segment).png', 'Seo::ogImage/$1'); // cached social cards // --------------------------------------------------------------------- // Public API (internal + v1). Noindex; blocked in robots.txt. // --------------------------------------------------------------------- $routes->group('api', ['namespace' => 'App\Controllers\Api'], static function ($routes) { // Browser-facing endpoints used by tool pages. $routes->post('jobs', 'Jobs::create'); $routes->get('jobs/(:segment)', 'Jobs::show/$1'); $routes->delete('jobs/(:segment)', 'Jobs::delete/$1'); $routes->post('youtube/info', 'Youtube::info'); $routes->post('events', 'Events::track'); // analytics beacon $routes->get('suggest', 'SearchApi::suggest'); // search-as-you-type // Versioned developer API (token auth). $routes->group('v1', ['filter' => 'api-auth'], static function ($routes) { $routes->get('tools', 'ToolsApi::index'); $routes->get('tools/(:segment)', 'ToolsApi::show/$1'); $routes->post('convert', 'Jobs::create'); $routes->get('jobs/(:segment)', 'Jobs::show/$1'); $routes->delete('jobs/(:segment)', 'Jobs::delete/$1'); }); }); // File upload endpoint for tool pages (multipart). $routes->post('/upload', 'Media::upload'); $routes->post('/jobs/start', 'Media::startJob'); // Result download endpoint — temporary, never indexed. $routes->get('/download/(:segment)', 'Media::download/$1'); $routes->get('/preview/(:segment).(:segment)', 'Media::preview/$1/$2'); // --------------------------------------------------------------------- // Static / legal pages // --------------------------------------------------------------------- $routes->get('/privacy', 'Pages::privacy'); $routes->get('/terms', 'Pages::terms'); $routes->get('/cookies', 'Pages::cookies'); $routes->get('/dmca', 'Pages::dmca'); $routes->get('/contact', 'Pages::contact'); // --------------------------------------------------------------------- // Search & directory // --------------------------------------------------------------------- $routes->get('/search', 'Search::index'); $routes->get('/tools', 'Directory::index'); $routes->get('/tools/popular', 'Directory::popular'); $routes->get('/tools/recent', 'Directory::recent'); $routes->get('/tools/a-z', 'Directory::az'); $routes->get('/tools/category/(:segment)', 'Directory::category/$1'); // --------------------------------------------------------------------- // Guides cluster // --------------------------------------------------------------------- $routes->get('/blog', 'Guides::index'); $routes->get('/blog/rss.xml', 'Guides::rss'); $routes->get('/blog/(:segment)', 'Guides::show/$1'); // --------------------------------------------------------------------- // Category landing pages: /youtube-tools, /video-tools, ... // --------------------------------------------------------------------- $routes->get('/([a-z0-9]+-tools)', 'Categories::show/$1'); // --------------------------------------------------------------------- // Admin panel (session auth, CSRF protected, noindex) // --------------------------------------------------------------------- $routes->group('admin', ['namespace' => 'App\Controllers\Admin', 'filter' => 'admin-auth'], static function ($routes) { $routes->get('/', 'Dashboard::index'); $routes->get('login', 'Auth::loginForm'); $routes->post('login', 'Auth::attempt'); $routes->get('logout', 'Auth::logout'); $routes->get('tools', 'ToolsAdmin::index'); $routes->get('tools/new', 'ToolsAdmin::create'); $routes->post('tools', 'ToolsAdmin::store'); $routes->get('tools/(:num)/edit', 'ToolsAdmin::edit/$1'); $routes->post('tools/(:num)', 'ToolsAdmin::update/$1'); $routes->post('tools/(:num)/delete', 'ToolsAdmin::delete/$1'); $routes->get('categories', 'CategoriesAdmin::index'); $routes->post('categories', 'CategoriesAdmin::store'); $routes->get('categories/(:num)/edit', 'CategoriesAdmin::edit/$1'); $routes->post('categories/(:num)', 'CategoriesAdmin::update/$1'); $routes->get('guides', 'GuidesAdmin::index'); $routes->get('guides/new', 'GuidesAdmin::create'); $routes->post('guides', 'GuidesAdmin::store'); $routes->get('guides/(:num)/edit', 'GuidesAdmin::edit/$1'); $routes->post('guides/(:num)', 'GuidesAdmin::update/$1'); $routes->post('guides/(:num)/delete', 'GuidesAdmin::delete/$1'); $routes->get('seo', 'SeoAdmin::index'); $routes->get('analytics', 'AnalyticsAdmin::index'); $routes->get('jobs', 'JobsAdmin::index'); $routes->post('jobs/(:segment)/retry', 'JobsAdmin::retry/$1'); $routes->get('system', 'SystemAdmin::index'); $routes->get('settings', 'SettingsAdmin::index'); $routes->post('settings', 'SettingsAdmin::save'); }); // --------------------------------------------------------------------- // Homepage last but one... // --------------------------------------------------------------------- $routes->get('/', 'Home::index'); // --------------------------------------------------------------------- // ...and the catch-all: every active tool slug lives at the root. // Registered last so concrete routes always win. Uppercase and // trailing-slash variants are canonicalized by the SeoFilter. // --------------------------------------------------------------------- $routes->get('/([a-z0-9-]+)', 'Tools::show/$1');