api/settings.php

<?php
declare(strict_types=1);

/*
|--------------------------------------------------------------------------
| Settings endpoint
|--------------------------------------------------------------------------
|
| Two actions, both POST + JSON body:
| - ?action=install : first-run setup (see wizard.js), only valid before
|   Brivacia is installed.
| - ?action=save    : update settings from the settings modal, requires
|   an authenticated admin session.
|
| Both funnel through brivacia_sanitize_settings() (includes/settings.php)
| before anything is written to settings.json.
|
*/

require_once __DIR__ . '/../includes/core.php';
require_once __DIR__ . '/../includes/modules/auth.php';

brivaciaSecurityHeaders();
header('Content-Type: application/json; charset=utf-8');
header('X-Robots-Tag: noindex, nofollow, noarchive, nosnippet');

function brivacia_api_settings_response(array $payload, int $status = 200): never
{
    http_response_code($status);
    echo json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
    exit;
}

function brivacia_api_settings_require_post(): void
{
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        brivacia_api_settings_response([
            'ok' => false,
            'error' => 'Method not allowed.',
        ], 405);
    }
}

function brivacia_request_json(): array
{
    $raw = file_get_contents('php://input');

    if ($raw === false || trim($raw) === '') {
        return $_POST;
    }

    $data = json_decode($raw, true);

    if (!is_array($data)) {
        brivacia_api_settings_response([
            'ok' => false,
            'error' => 'Invalid JSON payload.',
        ], 400);
    }

    return $data;
}

function brivacia_expand_dotted_input(array $input): array
{
    $expanded = [];

    foreach ($input as $key => $value) {
        if (!is_string($key) || !str_contains($key, '.')) {
            $expanded[$key] = $value;
            continue;
        }

        $target = &$expanded;

        foreach (explode('.', $key) as $part) {
            if (!isset($target[$part]) || !is_array($target[$part])) {
                $target[$part] = [];
            }

            $target = &$target[$part];
        }

        $target = $value;
        unset($target);
    }

    return $expanded;
}

$action = (string)($_GET['action'] ?? '');

try {
    /*
    |--------------------------------------------------------------------------
    | Read settings
    |--------------------------------------------------------------------------
    |
    | Public before installation so the wizard can initialize.
    | Private after installation because settings include admin configuration.
    |
    */
    if ($_SERVER['REQUEST_METHOD'] === 'GET') {
        if (brivacia_is_installed()) {
            brivaciaRequireApiAuth();
        }

        brivacia_api_settings_response([
            'ok' => true,
            'installed' => brivacia_is_installed(),
            'settings' => brivacia_public_settings(),
        ]);
    }

    /*
    |--------------------------------------------------------------------------
    | Initial installation
    |--------------------------------------------------------------------------
    |
    | This endpoint stays public only until Brivacia is installed.
    |
    */
    if ($action === 'install') {
        brivacia_api_settings_require_post();

        if (brivacia_is_installed()) {
            brivacia_api_settings_response([
                'ok' => false,
                'error' => 'Brivacia is already installed.',
            ], 409);
        }

        $settings = brivacia_install(
            brivacia_expand_dotted_input(brivacia_request_json())
        );

        unset($settings['admin']['password_hash']);

        brivacia_api_settings_response([
            'ok' => true,
            'installed' => true,
            'settings' => $settings,
        ]);
    }

    /*
    |--------------------------------------------------------------------------
    | Save settings
    |--------------------------------------------------------------------------
    |
    | Only an authenticated administrator may update settings.
    |
    */
    if ($action === 'save') {
        brivacia_api_settings_require_post();
        brivaciaRequireApiAuth();
        brivaciaRequireCsrf();

        if (!brivacia_is_installed()) {
            brivacia_api_settings_response([
                'ok' => false,
                'error' => 'Brivacia is not installed yet.',
            ], 403);
        }

        $current = brivacia_settings();
        $input = brivacia_expand_dotted_input(brivacia_request_json());

        unset(
            $input['installed'],
            $input['secret'],
            $input['key'],
            $input['admin']['password_hash'],
            $input['admin']['two_factor'],
            $input['admin']['two_factor_setup']
        );

        if (isset($input['referrers']['max_icon_mb'])) {
            $input['referrers']['max_icon_bytes'] = (int) round(
                ((float) $input['referrers']['max_icon_mb']) * 1048576
            );

            unset($input['referrers']['max_icon_mb']);
        }

        $settings = brivacia_sanitize_settings($input, $current);
        $settings['installed'] = true;

        brivacia_save_settings($settings);

        brivacia_api_settings_response([
            'ok' => true,
            'settings' => brivacia_public_settings(),
        ]);
    }

    brivacia_api_settings_response([
        'ok' => false,
        'error' => 'Unknown action.',
    ], 400);
} catch (Throwable $e) {
    brivacia_api_settings_response([
        'ok' => false,
        'error' => $e->getMessage(),
    ], 500);
}

Contribute