v3.0.0.2
FlatlyPage
- <?php
- require_once __DIR__ . '/../config.php';
- require_once __DIR__ . '/../admin/ai-helper-lib.php';
-
- header('Content-Type: application/json; charset=utf-8');
- header('Cache-Control: no-store');
- header('X-Content-Type-Options: nosniff');
-
- function ai_respond(int $status, array $payload): void
- {
- http_response_code($status);
- echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PARTIAL_OUTPUT_ON_ERROR);
- exit;
- }
-
- if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
- header('Allow: POST');
- ai_respond(405, ['ok' => false, 'error' => 'Method not allowed.']);
- }
-
- if (!is_logged_in()) {
- ai_respond(401, ['ok' => false, 'error' => 'Your session expired. Reload the page and sign in again.']);
- }
- check_ip_ban();
-
- if (!verify_csrf_token((string) ($_SERVER['HTTP_X_CSRF_TOKEN'] ?? ''))) {
- ai_respond(403, ['ok' => false, 'error' => 'Your session timed out. Reload the page and try again.']);
- }
-
- $maxBody = 400000;
- if ((int) ($_SERVER['CONTENT_LENGTH'] ?? 0) > $maxBody) {
- ai_respond(413, ['ok' => false, 'error' => 'This request is too large.']);
- }
- $input = json_decode((string) file_get_contents('php://input', false, null, 0, $maxBody + 1), true);
- if (!is_array($input)) {
- ai_respond(400, ['ok' => false, 'error' => 'Something went wrong. Reload the page and try again.']);
- }
-
- $now = time();
- $hits = array_values(array_filter((array) ($_SESSION['ai_helper_hits'] ?? []), static fn($time) => is_int($time) && $now - $time < 600));
- if (count($hits) >= 30) {
- ai_respond(429, ['ok' => false, 'error' => 'Too many AI requests in a short time. Please wait a few minutes.']);
- }
- $hits[] = $now;
- $_SESSION['ai_helper_hits'] = $hits;
- session_write_close();
- set_time_limit(180);
-
- $settings = AiHelper::settings();
- if (!$settings['enabled'] || $settings['key'] === '') {
- ai_respond(400, ['ok' => false, 'error' => 'The AI helper is turned off. Turn it on and add an API key in Settings, under AI Helper.']);
- }
-
- $action = (string) ($input['action'] ?? 'chat');
-
- if ($action === 'test') {
- $result = AiHelper::test($settings);
- ai_respond($result['ok'] ? 200 : 502, $result);
- }
-
- if ($action !== 'chat') {
- ai_respond(400, ['ok' => false, 'error' => 'Unknown action.']);
- }
-
- $prompt = trim((string) ($input['prompt'] ?? ''));
- if ($prompt === '' || mb_strlen($prompt) > 4000) {
- ai_respond(400, ['ok' => false, 'error' => 'Type a message of up to 4000 characters.']);
- }
-
- if (($input['mode'] ?? '') === 'settings') {
- $tab = (string) ($input['tab'] ?? '');
- $history = [];
- foreach (array_slice(is_array($input['history'] ?? null) ? array_values($input['history']) : [], -6) as $entry) {
- if (is_array($entry) && in_array($entry['role'] ?? '', ['user', 'assistant'], true) && is_string($entry['text'] ?? null)) {
- $history[] = ['role' => $entry['role'], 'text' => mb_substr($entry['text'], 0, 1500)];
- }
- }
- $language = AiHelper::cleanLanguage($input['language'] ?? null);
- $result = AiHelper::proposeSettings($settings, $tab, $input['settings'] ?? [], $prompt, $history, $language, is_array($input['source'] ?? null) ? $input['source'] : null);
- ai_respond($result['ok'] ? 200 : 502, $result);
- }
-
- $scope = (string) ($input['scope'] ?? 'all');
- if ($scope !== 'all' && preg_match('/^b\d{1,3}$/', $scope) !== 1) {
- ai_respond(400, ['ok' => false, 'error' => 'Invalid scope.']);
- }
-
- $blocks = $input['blocks'] ?? [];
- if (!is_array($blocks) || count($blocks) > 100) {
- ai_respond(400, ['ok' => false, 'error' => 'Invalid page data.']);
- }
- $blocks = array_values($blocks);
-
- $page = is_array($input['page'] ?? null) ? $input['page'] : [];
-
- $history = [];
- foreach (array_slice(is_array($input['history'] ?? null) ? array_values($input['history']) : [], -6) as $entry) {
- if (is_array($entry) && in_array($entry['role'] ?? '', ['user', 'assistant'], true) && is_string($entry['text'] ?? null)) {
- $history[] = ['role' => $entry['role'], 'text' => mb_substr($entry['text'], 0, 1500)];
- }
- }
-
- $language = AiHelper::cleanLanguage($input['language'] ?? null);
- $source = null;
- if ($language !== null && is_array($input['source'] ?? null)) {
- $source = $input['source'];
- if (!is_array($source['blocks'] ?? null) || count($source['blocks']) > 100) {
- $source = null;
- }
- }
-
- $result = AiHelper::propose($settings, $page, $blocks, $prompt, $scope, $history, $language, $source);
- ai_respond($result['ok'] ? 200 : 502, $result);
-