Files
amneziavpnphp/public/index.php
T
infosave2007 a52aba25d8 Add QR code support in API endpoints
- Updated /api/clients/create to return config and qr_code
- Updated /api/clients/{id}/details to include config and qr_code
- Added new endpoint /api/clients/{id}/qr for getting QR code only
- Added API_EXAMPLES.md with usage examples and integration code
- Updated README.md API documentation
2025-11-07 15:25:59 +03:00

1157 lines
32 KiB
PHP

<?php
/**
* Amnezia VPN Web Panel
* Main entry point
*/
session_name(getenv('SESSION_NAME') ?: 'amnezia_panel_session');
session_start();
// Load dependencies
require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../inc/Config.php';
require_once __DIR__ . '/../inc/DB.php';
require_once __DIR__ . '/../inc/Auth.php';
require_once __DIR__ . '/../inc/Router.php';
require_once __DIR__ . '/../inc/View.php';
require_once __DIR__ . '/../inc/VpnServer.php';
require_once __DIR__ . '/../inc/VpnClient.php';
require_once __DIR__ . '/../inc/Translator.php';
require_once __DIR__ . '/../inc/JWT.php';
// Load environment configuration
Config::load(__DIR__ . '/../.env');
// Test database connection
try {
DB::conn();
} catch (Throwable $e) {
die('Database connection error: ' . $e->getMessage());
}
// Seed admin user if not exists
try {
$adminEmail = Config::get('ADMIN_EMAIL');
$adminPass = Config::get('ADMIN_PASSWORD');
if ($adminEmail && $adminPass) {
Auth::seedAdmin($adminEmail, $adminPass);
}
} catch (Throwable $e) {
// Ignore errors
}
// Initialize translator
Translator::init();
// Initialize template engine
$user = Auth::user();
$appName = Config::get('APP_NAME', 'Amnezia VPN Panel');
View::init(__DIR__ . '/../templates', [
'app_name' => $appName,
'user' => $user,
'current_language' => Translator::getCurrentLanguage(),
'languages' => Translator::getSupportedLanguages(),
'current_uri' => $_SERVER['REQUEST_URI'] ?? '/dashboard',
't' => function($key, $params = []) {
return Translator::t($key, $params);
}
]);
// Helper function for redirects
function redirect(string $to): void {
header('Location: ' . $to);
exit;
}
// Helper function to require authentication
function requireAuth(): void {
if (!Auth::check()) {
redirect('/login');
}
}
// Helper function to require admin
function requireAdmin(): void {
requireAuth();
if (!Auth::isAdmin()) {
http_response_code(403);
echo 'Forbidden: Admin access required';
exit;
}
}
/**
* PUBLIC ROUTES
*/
// Home page
Router::get('/', function () {
if (!Auth::check()) {
redirect('/login');
}
redirect('/dashboard');
});
// Login page
Router::get('/login', function () {
if (Auth::check()) {
redirect('/dashboard');
}
View::render('login.twig');
});
Router::post('/login', function () {
$email = trim($_POST['email'] ?? '');
$password = $_POST['password'] ?? '';
if (Auth::login($email, $password)) {
redirect('/dashboard');
}
View::render('login.twig', ['error' => 'Invalid credentials']);
});
// Register page
Router::get('/register', function () {
if (Auth::check()) {
redirect('/dashboard');
}
View::render('register.twig');
});
Router::post('/register', function () {
$name = trim($_POST['name'] ?? '');
$email = trim($_POST['email'] ?? '');
$password = $_POST['password'] ?? '';
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
View::render('register.twig', ['error' => 'Invalid email address']);
return;
}
if (strlen($password) < 6) {
View::render('register.twig', ['error' => 'Password must be at least 6 characters']);
return;
}
try {
$success = Auth::register($name, $email, $password);
if ($success) {
Auth::login($email, $password);
redirect('/dashboard');
}
} catch (Throwable $e) {
// Email already exists or other error
}
View::render('register.twig', ['error' => 'Registration failed. Email may already be in use.']);
});
// Logout
Router::get('/logout', function () {
Auth::logout();
redirect('/login');
});
/**
* AUTHENTICATED ROUTES
*/
// Dashboard
Router::get('/dashboard', function () {
requireAuth();
$user = Auth::user();
// Get user's servers
$servers = VpnServer::listByUser($user['id']);
// Get user's clients
$clients = VpnClient::listByUser($user['id']);
View::render('dashboard.twig', [
'servers' => $servers,
'clients' => $clients,
]);
});
// Servers list
Router::get('/servers', function () {
requireAuth();
$user = Auth::user();
$servers = Auth::isAdmin()
? VpnServer::listAll()
: VpnServer::listByUser($user['id']);
View::render('servers/index.twig', ['servers' => $servers]);
});
// Create server page
Router::get('/servers/create', function () {
requireAuth();
View::render('servers/create.twig');
});
// Create server action
Router::post('/servers/create', function () {
requireAuth();
$user = Auth::user();
$name = trim($_POST['name'] ?? '');
$host = trim($_POST['host'] ?? '');
$port = (int)($_POST['port'] ?? 22);
$username = trim($_POST['username'] ?? 'root');
$password = $_POST['password'] ?? '';
if (empty($name) || empty($host) || empty($password)) {
View::render('servers/create.twig', ['error' => 'All fields are required']);
return;
}
try {
$serverId = VpnServer::create([
'user_id' => $user['id'],
'name' => $name,
'host' => $host,
'port' => $port,
'username' => $username,
'password' => $password,
]);
redirect('/servers/' . $serverId . '/deploy');
} catch (Exception $e) {
View::render('servers/create.twig', ['error' => $e->getMessage()]);
}
});
// Delete server action
Router::post('/servers/{id}/delete', function ($params) {
requireAuth();
$user = Auth::user();
$serverId = (int)$params['id'];
try {
$server = new VpnServer($serverId);
$serverData = $server->getData();
// Check ownership or admin
if ($serverData['user_id'] != $user['id'] && !Auth::isAdmin()) {
http_response_code(403);
echo 'Forbidden';
return;
}
$server->delete();
$_SESSION['success_message'] = 'Server deleted successfully';
redirect('/servers');
} catch (Exception $e) {
$_SESSION['error_message'] = $e->getMessage();
redirect('/servers');
}
});
// Deploy server page
Router::get('/servers/{id}/deploy', function ($params) {
requireAuth();
$serverId = (int)$params['id'];
try {
$server = new VpnServer($serverId);
$serverData = $server->getData();
// Check ownership
$user = Auth::user();
if ($serverData['user_id'] != $user['id'] && !Auth::isAdmin()) {
http_response_code(403);
echo 'Forbidden';
return;
}
View::render('servers/deploy.twig', ['server' => $serverData]);
} catch (Exception $e) {
http_response_code(404);
echo 'Server not found';
}
});
// Deploy server action (AJAX)
Router::post('/servers/{id}/deploy', function ($params) {
requireAuth();
header('Content-Type: application/json');
$serverId = (int)$params['id'];
try {
$server = new VpnServer($serverId);
$serverData = $server->getData();
// Check ownership
$user = Auth::user();
if ($serverData['user_id'] != $user['id'] && !Auth::isAdmin()) {
http_response_code(403);
echo json_encode(['error' => 'Forbidden']);
return;
}
$result = $server->deploy();
echo json_encode($result);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
});
// View server
Router::get('/servers/{id}', function ($params) {
requireAuth();
$serverId = (int)$params['id'];
try {
$server = new VpnServer($serverId);
$serverData = $server->getData();
// Check ownership
$user = Auth::user();
if ($serverData['user_id'] != $user['id'] && !Auth::isAdmin()) {
http_response_code(403);
echo 'Forbidden';
return;
}
// Get clients for this server
$clients = VpnClient::listByServer($serverId);
View::render('servers/view.twig', [
'server' => $serverData,
'clients' => $clients,
]);
} catch (Exception $e) {
http_response_code(404);
echo 'Server not found';
}
});
// Delete server
Router::post('/servers/{id}/delete', function ($params) {
requireAuth();
$serverId = (int)$params['id'];
try {
$server = new VpnServer($serverId);
$serverData = $server->getData();
// Check ownership
$user = Auth::user();
if ($serverData['user_id'] != $user['id'] && !Auth::isAdmin()) {
http_response_code(403);
echo 'Forbidden';
return;
}
$server->delete();
redirect('/servers');
} catch (Exception $e) {
redirect('/servers');
}
});
// Create client for server
Router::post('/servers/{id}/clients/create', function ($params) {
requireAuth();
$serverId = (int)$params['id'];
$clientName = trim($_POST['name'] ?? '');
if (empty($clientName)) {
redirect('/servers/' . $serverId . '?error=Client+name+is+required');
return;
}
try {
$server = new VpnServer($serverId);
$serverData = $server->getData();
// Check ownership
$user = Auth::user();
if ($serverData['user_id'] != $user['id'] && !Auth::isAdmin()) {
http_response_code(403);
echo 'Forbidden';
return;
}
$clientId = VpnClient::create($serverId, $user['id'], $clientName);
redirect('/clients/' . $clientId);
} catch (Exception $e) {
redirect('/servers/' . $serverId . '?error=' . urlencode($e->getMessage()));
}
});
// View client
Router::get('/clients/{id}', function ($params) {
requireAuth();
$clientId = (int)$params['id'];
try {
$client = new VpnClient($clientId);
$clientData = $client->getData();
// Check ownership
$user = Auth::user();
if ($clientData['user_id'] != $user['id'] && !Auth::isAdmin()) {
http_response_code(403);
echo 'Forbidden';
return;
}
View::render('clients/view.twig', ['client' => $clientData]);
} catch (Exception $e) {
http_response_code(404);
echo 'Client not found';
}
});
// Download client config
Router::get('/clients/{id}/download', function ($params) {
requireAuth();
$clientId = (int)$params['id'];
try {
$client = new VpnClient($clientId);
$clientData = $client->getData();
// Check ownership
$user = Auth::user();
if ($clientData['user_id'] != $user['id'] && !Auth::isAdmin()) {
http_response_code(403);
echo 'Forbidden';
return;
}
$config = $client->getConfig();
$filename = preg_replace('/[^a-zA-Z0-9_-]/', '_', $clientData['name']) . '.conf';
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Content-Length: ' . strlen($config));
echo $config;
} catch (Exception $e) {
http_response_code(404);
echo 'Client not found';
}
});
// Revoke client access
Router::post('/clients/{id}/revoke', function ($params) {
requireAuth();
$clientId = (int)$params['id'];
try {
$client = new VpnClient($clientId);
$clientData = $client->getData();
// Check ownership
$user = Auth::user();
if ($clientData['user_id'] != $user['id'] && !Auth::isAdmin()) {
http_response_code(403);
echo 'Forbidden';
return;
}
if ($client->revoke()) {
redirect('/servers/' . $clientData['server_id'] . '?success=Client+revoked');
} else {
redirect('/servers/' . $clientData['server_id'] . '?error=Failed+to+revoke+client');
}
} catch (Exception $e) {
redirect('/dashboard?error=' . urlencode($e->getMessage()));
}
});
// Restore client access
Router::post('/clients/{id}/restore', function ($params) {
requireAuth();
$clientId = (int)$params['id'];
try {
$client = new VpnClient($clientId);
$clientData = $client->getData();
// Check ownership
$user = Auth::user();
if ($clientData['user_id'] != $user['id'] && !Auth::isAdmin()) {
http_response_code(403);
echo 'Forbidden';
return;
}
if ($client->restore()) {
redirect('/servers/' . $clientData['server_id'] . '?success=Client+restored');
} else {
redirect('/servers/' . $clientData['server_id'] . '?error=Failed+to+restore+client');
}
} catch (Exception $e) {
redirect('/dashboard?error=' . urlencode($e->getMessage()));
}
});
// Delete client
Router::post('/clients/{id}/delete', function ($params) {
requireAuth();
$clientId = (int)$params['id'];
try {
$client = new VpnClient($clientId);
$clientData = $client->getData();
// Check ownership
$user = Auth::user();
if ($clientData['user_id'] != $user['id'] && !Auth::isAdmin()) {
http_response_code(403);
echo 'Forbidden';
return;
}
$serverId = $clientData['server_id'];
if ($client->delete()) {
redirect('/servers/' . $serverId . '?success=Client+deleted');
} else {
redirect('/servers/' . $serverId . '?error=Failed+to+delete+client');
}
} catch (Exception $e) {
redirect('/dashboard?error=' . urlencode($e->getMessage()));
}
});
// Sync client stats
Router::post('/clients/{id}/sync-stats', function ($params) {
requireAuth();
$clientId = (int)$params['id'];
header('Content-Type: application/json');
try {
$client = new VpnClient($clientId);
$clientData = $client->getData();
// Check ownership
$user = Auth::user();
if ($clientData['user_id'] != $user['id'] && !Auth::isAdmin()) {
http_response_code(403);
echo json_encode(['error' => 'Forbidden']);
return;
}
if ($client->syncStats()) {
// Reload client data
$client = new VpnClient($clientId);
$stats = $client->getFormattedStats();
echo json_encode(['success' => true, 'stats' => $stats]);
} else {
echo json_encode(['success' => false, 'error' => 'Failed to sync stats']);
}
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
});
// Sync all stats for server
Router::post('/servers/{id}/sync-stats', function ($params) {
requireAuth();
$serverId = (int)$params['id'];
header('Content-Type: application/json');
try {
$server = new VpnServer($serverId);
$serverData = $server->getData();
// Check ownership
$user = Auth::user();
if ($serverData['user_id'] != $user['id'] && !Auth::isAdmin()) {
http_response_code(403);
echo json_encode(['error' => 'Forbidden']);
return;
}
$synced = VpnClient::syncAllStatsForServer($serverId);
echo json_encode(['success' => true, 'synced' => $synced]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
});
/**
* API ROUTES (for Telegram bot integration)
*/
// API: Generate JWT token
Router::post('/api/auth/token', function () {
header('Content-Type: application/json');
$email = $_POST['email'] ?? '';
$password = $_POST['password'] ?? '';
if (empty($email) || empty($password)) {
http_response_code(400);
echo json_encode(['error' => 'Email and password are required']);
return;
}
$user = Auth::getUserByEmail($email);
if (!$user || !password_verify($password, $user['password_hash'])) {
http_response_code(401);
echo json_encode(['error' => 'Invalid credentials']);
return;
}
try {
$token = JWT::generate($user['id']);
echo json_encode([
'success' => true,
'token' => $token,
'type' => 'Bearer',
'expires_in' => 30 * 24 * 3600 // 30 days
]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['error' => 'Token generation failed']);
}
});
// API: Create persistent API token
Router::post('/api/tokens', function () {
header('Content-Type: application/json');
$user = JWT::requireAuth();
if (!$user) return;
$name = $_POST['name'] ?? 'API Token';
$expiresIn = isset($_POST['expires_in']) ? (int)$_POST['expires_in'] : 2592000; // 30 days default
try {
$tokenData = JWT::createApiToken($user['id'], $name, $expiresIn);
echo json_encode([
'success' => true,
'token' => $tokenData
]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
});
// API: List user's API tokens
Router::get('/api/tokens', function () {
header('Content-Type: application/json');
$user = JWT::requireAuth();
if (!$user) return;
$stmt = DB::get()->prepare("
SELECT id, name, token, expires_at, created_at, last_used_at
FROM api_tokens
WHERE user_id = ? AND revoked_at IS NULL
ORDER BY created_at DESC
");
$stmt->execute([$user['id']]);
$tokens = $stmt->fetchAll();
// Don't expose full token in list
foreach ($tokens as &$token) {
$token['token'] = substr($token['token'], 0, 10) . '...';
}
echo json_encode(['tokens' => $tokens]);
});
// API: Revoke API token
Router::delete('/api/tokens/{id}', function ($params) {
header('Content-Type: application/json');
$user = JWT::requireAuth();
if (!$user) return;
try {
JWT::revokeApiToken($params['id'], $user['id']);
echo json_encode(['success' => true]);
} catch (Exception $e) {
http_response_code(404);
echo json_encode(['error' => $e->getMessage()]);
}
});
// API: List servers
Router::get('/api/servers', function () {
header('Content-Type: application/json');
$user = JWT::requireAuth();
if (!$user) return;
$servers = VpnServer::listByUser($user['id']);
echo json_encode(['servers' => $servers]);
});
// API: Create server
Router::post('/api/servers/create', function () {
header('Content-Type: application/json');
$user = JWT::requireAuth();
if (!$user) return;
$input = json_decode(file_get_contents('php://input'), true);
$name = trim($input['name'] ?? '');
$host = trim($input['host'] ?? '');
$port = (int)($input['port'] ?? 22);
$username = trim($input['username'] ?? 'root');
$password = $input['password'] ?? '';
if (empty($name) || empty($host) || empty($password)) {
http_response_code(400);
echo json_encode(['error' => 'Missing required fields: name, host, password']);
return;
}
try {
$serverId = VpnServer::create([
'user_id' => $user['id'],
'name' => $name,
'host' => $host,
'port' => $port,
'username' => $username,
'password' => $password,
]);
http_response_code(201);
echo json_encode([
'success' => true,
'server_id' => $serverId,
'message' => 'Server created successfully'
]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
});
// API: Delete server
Router::delete('/api/servers/{id}/delete', function ($params) {
header('Content-Type: application/json');
$user = JWT::requireAuth();
if (!$user) return;
$serverId = (int)$params['id'];
try {
$server = new VpnServer($serverId);
$serverData = $server->getData();
// Check ownership or admin
if ($serverData['user_id'] != $user['id'] && $user['role'] !== 'admin') {
http_response_code(403);
echo json_encode(['error' => 'Forbidden']);
return;
}
$server->delete();
echo json_encode([
'success' => true,
'message' => 'Server deleted successfully'
]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
});
// API: List clients
Router::get('/api/clients', function () {
header('Content-Type: application/json');
$user = JWT::requireAuth();
if (!$user) return;
$clients = VpnClient::listByUser($user['id']);
echo json_encode(['clients' => $clients]);
});
// API: Get client details with stats
Router::get('/api/clients/{id}/details', function ($params) {
header('Content-Type: application/json');
$user = JWT::requireAuth();
if (!$user) return;
$clientId = (int)$params['id'];
try {
$client = new VpnClient($clientId);
$clientData = $client->getData();
// Check ownership
if ($clientData['user_id'] != $user['id']) {
http_response_code(403);
echo json_encode(['error' => 'Forbidden']);
return;
}
// Sync stats before returning
$client->syncStats();
// Reload data
$client = new VpnClient($clientId);
$clientData = $client->getData();
$stats = $client->getFormattedStats();
echo json_encode([
'success' => true,
'client' => [
'id' => $clientData['id'],
'name' => $clientData['name'],
'server_id' => $clientData['server_id'],
'client_ip' => $clientData['client_ip'],
'status' => $clientData['status'],
'created_at' => $clientData['created_at'],
'stats' => $stats,
'bytes_sent' => $clientData['bytes_sent'],
'bytes_received' => $clientData['bytes_received'],
'last_handshake' => $clientData['last_handshake'],
'config' => $clientData['config'],
'qr_code' => $clientData['qr_code'],
]
]);
} catch (Exception $e) {
http_response_code(404);
echo json_encode(['error' => 'Client not found']);
}
});
// API: Get client QR code
Router::get('/api/clients/{id}/qr', function ($params) {
header('Content-Type: application/json');
$user = JWT::requireAuth();
if (!$user) return;
$clientId = (int)$params['id'];
try {
$client = new VpnClient($clientId);
$clientData = $client->getData();
// Check ownership
if ($clientData['user_id'] != $user['id']) {
http_response_code(403);
echo json_encode(['error' => 'Forbidden']);
return;
}
echo json_encode([
'success' => true,
'qr_code' => $clientData['qr_code'],
'client_name' => $clientData['name']
]);
} catch (Exception $e) {
http_response_code(404);
echo json_encode(['error' => 'Client not found']);
}
});
// API: Revoke client
Router::post('/api/clients/{id}/revoke', function ($params) {
header('Content-Type: application/json');
$user = JWT::requireAuth();
if (!$user) return;
$clientId = (int)$params['id'];
try {
$client = new VpnClient($clientId);
$clientData = $client->getData();
// Check ownership
if ($clientData['user_id'] != $user['id']) {
http_response_code(403);
echo json_encode(['error' => 'Forbidden']);
return;
}
if ($client->revoke()) {
echo json_encode(['success' => true, 'message' => 'Client revoked']);
} else {
http_response_code(500);
echo json_encode(['error' => 'Failed to revoke client']);
}
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
});
// API: Restore client
Router::post('/api/clients/{id}/restore', function ($params) {
header('Content-Type: application/json');
$user = JWT::requireAuth();
if (!$user) return;
$clientId = (int)$params['id'];
try {
$client = new VpnClient($clientId);
$clientData = $client->getData();
// Check ownership
if ($clientData['user_id'] != $user['id']) {
http_response_code(403);
echo json_encode(['error' => 'Forbidden']);
return;
}
if ($client->restore()) {
echo json_encode(['success' => true, 'message' => 'Client restored']);
} else {
http_response_code(500);
echo json_encode(['error' => 'Failed to restore client']);
}
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
});
// API: Get server clients
Router::get('/api/servers/{id}/clients', function ($params) {
header('Content-Type: application/json');
$user = JWT::requireAuth();
if (!$user) return;
$serverId = (int)$params['id'];
try {
$server = new VpnServer($serverId);
$serverData = $server->getData();
// Check ownership
$user = Auth::user();
if ($serverData['user_id'] != $user['id'] && !Auth::isAdmin()) {
http_response_code(403);
echo json_encode(['error' => 'Forbidden']);
return;
}
// Sync all stats first
VpnClient::syncAllStatsForServer($serverId);
$clients = VpnClient::listByServer($serverId);
$clientsData = [];
foreach ($clients as $clientData) {
$client = new VpnClient($clientData['id']);
$stats = $client->getFormattedStats();
$clientsData[] = [
'id' => $clientData['id'],
'name' => $clientData['name'],
'client_ip' => $clientData['client_ip'],
'status' => $clientData['status'],
'created_at' => $clientData['created_at'],
'stats' => $stats,
'bytes_sent' => $clientData['bytes_sent'],
'bytes_received' => $clientData['bytes_received'],
'last_handshake' => $clientData['last_handshake'],
];
}
echo json_encode(['success' => true, 'clients' => $clientsData]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
});
// API: Create client
Router::post('/api/clients/create', function () {
header('Content-Type: application/json');
$user = JWT::requireAuth();
if (!$user) return;
$raw = file_get_contents('php://input');
$data = json_decode($raw, true);
$serverId = (int)($data['server_id'] ?? 0);
$name = trim($data['name'] ?? '');
if ($serverId <= 0 || empty($name)) {
http_response_code(400);
echo json_encode(['error' => 'server_id and name are required']);
return;
}
try {
$clientId = VpnClient::create($serverId, $user['id'], $name);
$client = new VpnClient($clientId);
$clientData = $client->getData();
// Return client data with config and QR code
echo json_encode([
'success' => true,
'client' => [
'id' => $clientData['id'],
'name' => $clientData['name'],
'server_id' => $clientData['server_id'],
'client_ip' => $clientData['client_ip'],
'status' => $clientData['status'],
'created_at' => $clientData['created_at'],
'config' => $clientData['config'],
'qr_code' => $clientData['qr_code'],
]
]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
});
/**
* SETTINGS ROUTES
*/
// Settings page
Router::get('/settings', function () {
requireAuth();
require_once __DIR__ . '/../controllers/SettingsController.php';
$controller = new SettingsController();
$controller->index();
});
// Save API key
Router::post('/settings/api-key', function () {
requireAdmin();
require_once __DIR__ . '/../controllers/SettingsController.php';
$controller = new SettingsController();
$controller->saveApiKey();
});
// Change password
Router::post('/settings/change-password', function () {
requireAuth();
require_once __DIR__ . '/../controllers/SettingsController.php';
$controller = new SettingsController();
$controller->changePassword();
});
// Add user
Router::post('/settings/add-user', function () {
requireAdmin();
require_once __DIR__ . '/../controllers/SettingsController.php';
$controller = new SettingsController();
$controller->addUser();
});
// Delete user
Router::post('/settings/delete-user/{id}', function ($params) {
requireAdmin();
require_once __DIR__ . '/../controllers/SettingsController.php';
$controller = new SettingsController();
$controller->deleteUser($params['id']);
});
/**
* LANGUAGE ROUTES
*/
// Change language
Router::post('/language/change', function () {
$lang = $_POST['language'] ?? '';
if (Translator::setLanguage($lang)) {
$_SESSION['success'] = 'Language changed successfully';
} else {
$_SESSION['error'] = 'Invalid language';
}
$redirect = $_POST['redirect'] ?? '/dashboard';
redirect($redirect);
});
Router::get('/language/change', function () {
redirect('/dashboard');
});
// API: Get translation statistics
Router::get('/api/translations/stats', function () {
header('Content-Type: application/json');
$user = JWT::requireAuth();
if (!$user) return;
$stats = Translator::getStatistics();
echo json_encode(['stats' => $stats]);
});
// API: Auto-translate missing keys
Router::post('/api/translations/auto-translate', function () {
header('Content-Type: application/json');
$user = JWT::requireAuth();
if (!$user) return;
$raw = file_get_contents('php://input');
$data = json_decode($raw, true);
$targetLang = $data['language'] ?? '';
if (empty($targetLang)) {
http_response_code(400);
echo json_encode(['error' => 'Language is required']);
return;
}
try {
$stats = Translator::translateMissingKeys($targetLang);
echo json_encode([
'success' => true,
'stats' => $stats
]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
});
// API: Export translations
Router::get('/api/translations/export/{lang}', function ($params) {
header('Content-Type: application/json');
$user = JWT::requireAuth();
if (!$user) return;
$lang = $params['lang'];
try {
$json = Translator::exportToJson($lang);
header('Content-Disposition: attachment; filename="translations_' . $lang . '.json"');
echo $json;
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
});
// Dispatch router
Router::dispatch($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI']);