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);
}