Added backups of all data, restorable on another computer (#1008)

Co-authored-by: Thorsten Sommer <SommerEngineering@users.noreply.github.com>
This commit is contained in:
j-erlerandThorsten Sommer authored and GitHub committed 2026-10-15 16:42:02 +02:00
1 parent ab45580473
commit 936043319c
144 files changed
+10417 -424

No files matched your search

@@ -421,11 +421,7 @@ public record ChatTemplate(
}
var combinedPath = Path.GetFullPath(Path.Combine(pluginRoot, relativePath));
var pluginRootWithSeparator = pluginRoot.EndsWith(Path.DirectorySeparatorChar)
? pluginRoot
: pluginRoot + Path.DirectorySeparatorChar;
var comparison = OperatingSystem.IsWindows() ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
if (!combinedPath.StartsWith(pluginRootWithSeparator, comparison))
if (!PathTools.IsInsideDirectory(pluginRoot, combinedPath))
{
LOGGER.LogWarning("The relative FileAttachments entry {AttachmentNum} in chat template {IdxChatTemplate} points outside of the plugin folder and will be ignored.", attachmentNum, idx);
return false;
@@ -116,6 +116,16 @@ public sealed class Data
/// </remarks>
public List<string> AppliedEnterpriseAssistantActivations { get; set; } = [];
/// <summary>
/// Whether AI Studio already stored the chats and the settings once more, so that every path below
/// the data directory in them is relative.
/// </summary>
/// <remarks>
/// Compatibility shim, see documentation/compatibility-shims/2026-10-relative-data-paths.md.
/// Remove after 2027-04-14.
/// </remarks>
public bool StoredDataPathsAreRelative { get; set; }
/// <summary>
/// The next provider number to use.
/// </summary>
@@ -219,6 +219,24 @@ public sealed class DataApp(Expression<Func<Data, DataApp>>? configSelection = n
/// </summary>
public bool AllowUserToSharePlugins { get; set; } = ManagedConfiguration.Register(configSelection, n => n.AllowUserToSharePlugins, true);
/// <summary>
/// Should the user be allowed to back up the data of AI Studio into a file?
/// </summary>
/// <remarks>
/// A backup holds chats and other confidential content in a single file, which can leave the
/// computer on any stick or cloud drive. Secrets never go into it.
/// </remarks>
public bool AllowUserToBackUpData { get; set; } = ManagedConfiguration.Register(configSelection, n => n.AllowUserToBackUpData, true);
/// <summary>
/// Should the user be allowed to restore the data of AI Studio from a backup file, or to add the chats of one?
/// </summary>
/// <remarks>
/// A backup brings along everything its chats and settings name, including the locations of
/// attached documents and of local data sources, which are read again afterward.
/// </remarks>
public bool AllowUserToRestoreData { get; set; } = ManagedConfiguration.Register(configSelection, n => n.AllowUserToRestoreData, true);
/// <summary>
/// Should administration settings be visible in the UI?
/// </summary>
@@ -22,7 +22,7 @@ public sealed record EmbeddingProvider(
Guid EnterpriseConfigurationPluginId = default,
string Hostname = "http://localhost:1234",
Host Host = Host.NONE,
string TokenizerPath = "",
[property: JsonConverter(typeof(DataDirectoryRelativePathConverter))] string TokenizerPath = "",
string TokenizerFingerprint = "",
int EmbeddingBatchSize = 0,
int TokenLimit = 0,
@@ -56,7 +56,7 @@ internal static class PluginIconFile
return false;
}
if (!IsInsideDirectory(pluginRoot, resolvedPath))
if (!PathTools.IsInsideDirectory(pluginRoot, resolvedPath))
{
issue = "The icon path points outside of the configuration plugin directory.";
return false;
@@ -108,18 +108,10 @@ internal static class PluginIconFile
currentPath = Path.Combine(currentPath, segment);
FileSystemInfo pathInfo = Directory.Exists(currentPath) ? new DirectoryInfo(currentPath) : new FileInfo(currentPath);
var finalTarget = pathInfo.ResolveLinkTarget(true);
if (finalTarget is not null && !IsInsideDirectory(rootDirectory, finalTarget.FullName))
if (finalTarget is not null && !PathTools.IsInsideDirectory(rootDirectory, finalTarget.FullName))
return false;
}
return true;
}
private static bool IsInsideDirectory(string rootDirectory, string path)
{
var root = Path.GetFullPath(rootDirectory).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + Path.DirectorySeparatorChar;
var target = Path.GetFullPath(path);
var comparison = OperatingSystem.IsWindows() ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
return target.StartsWith(root, comparison);
}
}
+1 -1
View File
@@ -36,7 +36,7 @@ public sealed record Provider(
Host Host = Host.NONE,
HFInferenceProvider HFInferenceProvider = HFInferenceProvider.NONE,
string AdditionalJsonApiParameters = "",
string TokenizerPath = "",
[property: JsonConverter(typeof(DataDirectoryRelativePathConverter))] string TokenizerPath = "",
ProviderCapabilityOverrides? CapabilityOverrides = null,
bool AllowUserProvidedAPIKey = false,
string CustomIconDataUrl = "") : ConfigurationBaseObject, ISecretId, IUserProvidedAPIKey