Migrate storage to JSON and add robust client-side save error handling

main
Steffen Pohle 2 weeks ago
parent 7a40c6f603
commit 84ab5c4303

@ -167,11 +167,11 @@ function sanitizeUsername($username) {
}
function getUserConfigPath($username) {
return USER_CONFIG_DIR . '/' . sanitizeUsername($username) . '-config.php';
return USER_CONFIG_DIR . '/' . sanitizeUsername($username) . '-config.json';
}
function getUserChatPath($username) {
return USER_CONFIG_DIR . '/' . sanitizeUsername($username) . '-chat.php';
return USER_CONFIG_DIR . '/' . sanitizeUsername($username) . '-chat.json';
}
function getConfiguredDefaultModel() {
@ -188,11 +188,30 @@ function getDefaultModelForUser($username) {
function readUserConfig($username) {
$path = getUserConfigPath($username);
if (!file_exists($path)) {
// Try migrating from legacy PHP file if present
$legacy = USER_CONFIG_DIR . '/' . sanitizeUsername($username) . '-config.php';
if (file_exists($legacy)) {
$raw = @file_get_contents($legacy);
if ($raw !== false) {
// attempt to include safely
$data = @include $legacy;
if (is_array($data)) {
// write JSON migration
writeUserConfig($username, $data);
return $data;
}
}
}
return [];
}
$raw = @file_get_contents($path);
if ($raw === false) {
return [];
}
$data = include $path;
return is_array($data) ? $data : [];
$decoded = json_decode($raw, true);
return is_array($decoded) ? $decoded : [];
}
function writeUserConfig($username, $config) {
@ -202,7 +221,6 @@ function writeUserConfig($username, $config) {
if ($username === '') {
return false;
}
$data = is_array($config) ? $config : [];
$data['username'] = $username;
$data['role'] = (string) ($data['role'] ?? 'user');
@ -211,18 +229,41 @@ function writeUserConfig($username, $config) {
$storedDefaultModel = trim((string) ($data['defaultModel'] ?? ''));
$data['defaultModel'] = $storedDefaultModel !== '' ? $storedDefaultModel : getConfiguredDefaultModel();
$php = "<?php\n\nreturn " . var_export($data, true) . ";\n";
return file_put_contents(getUserConfigPath($username), $php) !== false;
$json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
if ($json === false) {
return false;
}
$path = getUserConfigPath($username);
$tmp = $path . '.tmp';
if (@file_put_contents($tmp, $json . "\n") === false) {
return false;
}
return @rename($tmp, $path);
}
function readUserChat($username) {
$path = getUserChatPath($username);
if (!file_exists($path)) {
// Try migrating legacy PHP chat file
$legacy = USER_CONFIG_DIR . '/' . sanitizeUsername($username) . '-chat.php';
if (file_exists($legacy)) {
$data = @include $legacy;
if (is_array($data)) {
writeUserChat($username, $data);
return $data;
}
}
return [];
}
$data = include $path;
return is_array($data) ? $data : [];
$raw = @file_get_contents($path);
if ($raw === false) {
return [];
}
$decoded = json_decode($raw, true);
return is_array($decoded) ? $decoded : [];
}
function writeUserChat($username, $chats) {
@ -232,9 +273,17 @@ function writeUserChat($username, $chats) {
if ($username === '') {
return false;
}
$json = json_encode(is_array($chats) ? $chats : [], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
if ($json === false) {
return false;
}
$php = "<?php\n\nreturn " . var_export(is_array($chats) ? $chats : [], true) . ";\n";
return file_put_contents(getUserChatPath($username), $php) !== false;
$path = getUserChatPath($username);
$tmp = $path . '.tmp';
if (@file_put_contents($tmp, $json . "\n") === false) {
return false;
}
return @rename($tmp, $path);
}
function renameUserFiles($oldUsername, $newUsername) {
@ -281,21 +330,47 @@ function saveUserConfigData($username, $config) {
function loadUsers() {
ensureConfigDirectory();
$files = glob(USER_CONFIG_DIR . '/*-config.php');
$files = glob(USER_CONFIG_DIR . '/*-config.json');
if ($files === false) {
return [];
$files = [];
}
$users = [];
// load json config files
foreach ($files as $file) {
$username = basename($file, '-config.php');
$username = basename($file, '-config.json');
$user = readUserConfig($username);
if (!empty($user) && !empty($user['username'])) {
$users[] = $user;
}
}
// detect legacy php config files and migrate
$legacyFiles = glob(USER_CONFIG_DIR . '/*-config.php');
if ($legacyFiles !== false) {
foreach ($legacyFiles as $legacy) {
$username = basename($legacy, '-config.php');
// if already loaded from json, skip
$exists = false;
foreach ($users as $u) {
if (($u['username'] ?? '') === $username) {
$exists = true;
break;
}
}
if ($exists) continue;
$data = @include $legacy;
if (is_array($data) && !empty($data['username'])) {
// migrate to json
writeUserConfig($username, $data);
// remove legacy files
@unlink($legacy);
$users[] = $data;
}
}
}
usort($users, function ($a, $b) {
return strcmp(($a['username'] ?? ''), ($b['username'] ?? ''));
});
@ -340,11 +415,13 @@ function saveUsers($users, $previousUsers = null) {
$currentUsernames[] = sanitizeUsername($entry['username']);
}
$files = glob(USER_CONFIG_DIR . '/*-config.php');
$files = glob(USER_CONFIG_DIR . '/*-config.json');
if ($files !== false) {
foreach ($files as $file) {
$name = basename($file, '-config.php');
$name = basename($file, '-config.json');
if (!in_array($name, $currentUsernames, true)) {
@unlink(USER_CONFIG_DIR . '/' . $name . '-config.json');
@unlink(USER_CONFIG_DIR . '/' . $name . '-chat.json');
@unlink(USER_CONFIG_DIR . '/' . $name . '-config.php');
@unlink(USER_CONFIG_DIR . '/' . $name . '-chat.php');
}

@ -207,9 +207,26 @@ async function saveChat() {
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id: currentChatId, model: modelSelect.value, messages: chatHistory })
});
const result = await response.json();
if (result.id) currentChatId = result.id;
loadHistory();
let result = null;
try {
result = await response.json();
} catch (e) {
console.error('Failed to parse save response', e);
const text = await response.text();
console.error('Raw save response:', text);
throw new Error('Save failed: invalid server response');
}
console.debug('Save response:', result);
if (result && result.error) {
console.error('Save error:', result.error);
alert('Chat save failed: ' + result.error);
throw new Error(result.error);
}
if (result && result.id) currentChatId = result.id;
await loadHistory();
return result;
} catch (err) {
console.error(err);
}

Loading…
Cancel
Save