File: /home/xedaptot/ai.naniguide.com/routes/api.php
<?php
use Illuminate\Http\Request;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::group(['namespace' => '\App\Http\Controllers\Api', 'prefix' => 'v1', 'middleware' => ['auth:api']], function () {
// Scalable FileBrowser backend — see /home/nghi/scalable-filemanager/README.md
Route::prefix('filemanager')->group(function () {
Route::get('list', 'FileManagerApiController@list')->name('api.filemanager.list');
Route::post('upload', 'FileManagerApiController@upload')->name('api.filemanager.upload');
Route::delete('delete', 'FileManagerApiController@delete')->name('api.filemanager.delete');
Route::patch('rename', 'FileManagerApiController@rename')->name('api.filemanager.rename');
Route::get('view', 'FileManagerApiController@view')->name('api.filemanager.view');
Route::post('create-file', 'FileManagerApiController@createFile')->name('api.filemanager.create-file');
});
// Dedicated endpoint hosting DirectoryAssetManager (base_path()). Admin-only.
Route::prefix('app-directory')->group(function () {
Route::get('list', 'AppDirectoryApiController@list')->name('api.app-directory.list');
Route::post('upload', 'AppDirectoryApiController@upload')->name('api.app-directory.upload');
Route::delete('delete', 'AppDirectoryApiController@delete')->name('api.app-directory.delete');
Route::patch('rename', 'AppDirectoryApiController@rename')->name('api.app-directory.rename');
Route::post('create-file', 'AppDirectoryApiController@createFile')->name('api.app-directory.create-file');
});
//
Route::get('', function () {
return \Response::json(\Auth::guard('api')->user());
});
// Simple authentication
Route::get('me', function () {
return \Response::json(\Auth::guard('api')->user());
});
// Identity + instance metadata — used by support tooling (e.g. `php artisan
// remote:upgrade --min-version=X`) to read current Acelle version cheaply
// without running an upgrade dry-run. Lives in core so it works on a vanilla
// install — no acelle/console plugin required.
Route::get('whoami', 'WhoamiController')->name('api.whoami');
// List
Route::delete('lists/{uid}', 'MailListController@delete');
Route::post('lists/{uid}/add-field', 'MailListController@addField');
Route::resource('lists', 'MailListController')->names([
'index' => 'api.lists.index',
'create' => 'api.lists.create',
'store' => 'api.lists.store',
'show' => 'api.lists.show',
'edit' => 'api.lists.edit',
'update' => 'api.lists.update',
'destroy' => 'api.lists.destroy',
]);
// Campaign
Route::delete('campaigns/{uid}', 'CampaignController@delete');
Route::post('campaigns/{uid}/pause', 'CampaignController@pause');
Route::post('campaigns/{uid}/run', 'CampaignController@run');
Route::post('campaigns/{uid}/resume', 'CampaignController@resume');
Route::resource('campaigns', 'CampaignController')->names([
'index' => 'api.campaigns.index',
'create' => 'api.campaigns.create',
'store' => 'api.campaigns.store',
'show' => 'api.campaigns.show',
'edit' => 'api.campaigns.edit',
'update' => 'api.campaigns.update',
'destroy' => 'api.campaigns.destroy',
]);
Route::get('campaigns/{uid}/tracking-log/download', 'CampaignController@downloadTrackingLog');
Route::get('campaigns/{uid}/open-log/download', 'CampaignController@downloadOpenLog');
Route::get('campaigns/{uid}/click-log/download', 'CampaignController@downloadClickLog');
Route::get('campaigns/{uid}/bounce-log/download', 'CampaignController@downloadBounceLog');
Route::get('campaigns/{uid}/feedback-log/download', 'CampaignController@downloadFeedbackLog');
Route::get('campaigns/{uid}/unsubscribe-log/download', 'CampaignController@downloadUnsubscribeLog');
// Subscriber
Route::patch('lists/{list_uid}/subscribers/email/{email}/unsubscribe', 'SubscriberController@unsubscribeEmail');
Route::post('subscribers/{id}/add-tag', 'SubscriberController@addTag');
Route::post('subscribers/{id}/remove-tag', 'SubscriberController@removeTag');
Route::get('subscribers/email/{email}', 'SubscriberController@showByEmail');
Route::patch('lists/{list_uid}/subscribers/{id}/subscribe', 'SubscriberController@subscribe');
Route::patch('lists/{list_uid}/subscribers/{id}/unsubscribe', 'SubscriberController@unsubscribe');
Route::delete('subscribers/{id}', 'SubscriberController@delete');
Route::resource('subscribers', 'SubscriberController')->names([
'index' => 'api.subscribers.index',
'create' => 'api.subscribers.create',
'store' => 'api.subscribers.store',
'show' => 'api.subscribers.show',
'edit' => 'api.subscribers.edit',
'update' => 'api.subscribers.update',
'destroy' => 'api.subscribers.destroy',
]);
// Bulk subscriber endpoints. Sync path here; async + status polling +
// cancel sit on SubscribersBulkAsyncController. Write endpoints are
// throttled (60/min) so a misbehaving integration can't lap the queue
// worker or starve the DB; GET endpoints stay unthrottled because they
// are typically polled at 1-2 Hz from job-status dashboards.
Route::middleware('throttle:60,1')->group(function () {
Route::post('lists/{list_uid}/subscribers/bulk', 'SubscribersBulkController@store')
->name('api.lists.subscribers.bulk');
Route::post('lists/{list_uid}/subscribers/bulk/async', 'SubscribersBulkAsyncController@store')
->name('api.lists.subscribers.bulk_async');
Route::delete('import-jobs/{job_uid}', 'SubscribersBulkAsyncController@destroy')
->name('api.import_jobs.destroy');
});
Route::get('lists/{list_uid}/import-jobs', 'SubscribersBulkAsyncController@indexForList')
->name('api.lists.import_jobs.index');
Route::get('import-jobs/{job_uid}', 'SubscribersBulkAsyncController@show')
->name('api.import_jobs.show');
// Automation
Route::post('automations/{uid}/api/call', 'AutomationController@apiCall');
// Sending server
Route::resource('sending_servers', 'SendingServerController')->names([
'index' => 'api.sending_servers.index',
'create' => 'api.sending_servers.create',
'store' => 'api.sending_servers.store',
'show' => 'api.sending_servers.show',
'edit' => 'api.sending_servers.edit',
'update' => 'api.sending_servers.update',
'destroy' => 'api.sending_servers.destroy',
]);
// Customer
Route::get('customers/by-email/{email}', 'CustomerController@findByEmail');
Route::post('customers/{uid}/subscription/update', 'CustomerController@subscriptionUpdate');
Route::post('customers/{uid}/change-plan/{plan_uid}', 'CustomerController@changePlan');
Route::match(['get','post'], 'login-token', 'CustomerController@loginToken');
Route::post('customers/{uid}/assign-plan/{plan_uid}', 'CustomerController@assignPlan');
Route::patch('customers/{uid}/disable', 'CustomerController@disable');
Route::patch('customers/{uid}/enable', 'CustomerController@enable');
Route::resource('customers', 'CustomerController')->names([
'index' => 'api.customers.index',
'create' => 'api.customers.create',
'store' => 'api.customers.store',
'show' => 'api.customers.show',
'edit' => 'api.customers.edit',
'update' => 'api.customers.update',
'destroy' => 'api.customers.destroy',
]);
// Subscription
Route::resource('subscriptions', 'SubscriptionController')->names([
'index' => 'api.subscriptions.index',
'create' => 'api.subscriptions.create',
'store' => 'api.subscriptions.store',
'show' => 'api.subscriptions.show',
'edit' => 'api.subscriptions.edit',
'update' => 'api.subscriptions.update',
'destroy' => 'api.subscriptions.destroy',
]);
// File
Route::post('file/upload', 'FileController@upload');
// File
Route::post('automations/{uid}/execute', 'AutomationController@execute')->name('automation_execute');
Route::post('notification/bounce', 'NotificationController@bounce');
Route::post('notification/feedback', 'NotificationController@feedback');
// User
Route::get('user/info', 'UserController@info');
// Dashboard
Route::get('dashboard', 'DashboardController@index');
// Automation
Route::get('automations', 'AutomationController@index');
// Plugin install from download URL (admin only; authorization enforced inside controller)
Route::post('plugins/install', 'PluginController@install')->name('api.plugins.install');
// Remote core upgrade — MUST be called as two separate HTTP requests
// (step 2 needs a fresh PHP worker that loads the newly-replaced files).
// See .support/HANDBOOK_DETAILS.md § Method API.
Route::post('upgrade/run', 'UpgradeController@run')->name('api.upgrade.run');
Route::post('upgrade/run-file', 'UpgradeController@runFile')->name('api.upgrade.run_file');
Route::post('upgrade/finalize', 'UpgradeController@finalize')->name('api.upgrade.finalize');
// License refresh (mirrors Admin → Settings → License → Refresh button)
Route::post('license/refresh', 'LicenseController@refresh')->name('api.license.refresh');
// `api.ai.obs.export` moved into the acelle/ai plugin's routes.php
// (W4 of the AI-extraction plan).
});
Route::group(['namespace' => '\App\Http\Controllers\Api', 'prefix' => 'v1', 'middleware' => []], function () {
// User
Route::post('user/login', 'UserController@login');
});
// AI Task framework — moved into the acelle/ai plugin's routes.php
// (W4 of the AI-extraction plan). The `/api/v1/ai/*` route group + the
// `api.ai.*` named routes are now owned by `Acelle\Ai\ServiceProvider`.
// Hub AI passthrough — main-app endpoint used by downstream Acelle
// instances to forward AI calls through this instance's local Claude
// Code CLI binary. Hub is OWNED BY MAIN APP (not the plugin) because
// the engine, config, and observability story is operator-level — see
// `App\Http\Controllers\Api\AIHubController` + ENV `HUB_AI_CLAUDE_CLI_*`.
// Plugin must be installed for the `ClaudeCLIClient` class.
Route::group(['namespace' => '\App\Http\Controllers\Api', 'prefix' => 'v1', 'middleware' => ['auth:api']], function () {
Route::post('hub-ai/handle', 'AIHubController@handle')->name('api.hub_ai.handle');
});
Route::group(['namespace' => '\App\Http\Controllers\Api\Public', 'prefix' => 'v1'], function () {
Route::post('public/subscribers', 'SubscriberController@store');
// Email Verification
Route::get('email-verification/get-checkout-url', 'EmailVerificationController@getCheckoutUrl');
Route::get('email-verification/get-subscription', 'EmailVerificationController@getSubscription');
Route::post('email-verification/customer/find-create', 'EmailVerificationController@findOrCreateCustomer');
Route::get('email-verification/feature-plan', 'EmailVerificationController@getFeaturePlan');
Route::post('email-verification/subscribe', 'EmailVerificationController@subscribe');
});
// Athena EVS routes have been moved to the plugin: storage/app/plugins/athena/evs/routes.php