Stufe 1 (#764): Werkzeugeinstellungen, Sicherungszeitplan und Import als neue Instanz
- Werkzeugformulare aus den typisierten Einstellungsklassen (L8): Geheimnisse
nur schreibend, nur geaenderte Schluessel werden ersetzt, Rechte bleiben
- Database.allowedTables las ein JSON-Array als rohen Text und haette es beim
naechsten Speichern zerlegt
- Automatische Sicherung der Instanz in der Sicherungsansicht (L10)
- Sicherung hochladen und als neue Instanz einspielen (L11): Pfade der alten
Maschine werden zurueckgesetzt, eine Kopie neben dem Original bekommt eine
eigene ID, Autostart aus; Upload bis 4 GB
- api.js schickte eine Datei als "{}"; Dialoge schliessen beim Seitenwechsel
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
8552755bb6
commit
d2559c763a
@@ -102,6 +102,12 @@ public sealed record ManagerSettingsUpdateDto(string? DeploymentcenterUrl, strin
|
||||
/// </summary>
|
||||
internal static class ManagerEndpoints
|
||||
{
|
||||
/// <summary>
|
||||
/// #764: Upper limit for an uploaded backup. Kestrel's default of 30 MB is far below a
|
||||
/// backup with chat histories and state.db; without a limit, an upload could fill the disk.
|
||||
/// </summary>
|
||||
private const long MaxImportBytes = 4L * 1024 * 1024 * 1024;
|
||||
|
||||
// ─── Was den Manager selbst angeht ───
|
||||
|
||||
public static void MapVerwaltung(this WebApplication app)
|
||||
@@ -382,6 +388,57 @@ internal static class ManagerEndpoints
|
||||
}
|
||||
});
|
||||
|
||||
// ─── Import a backup as a new instance (#764) ───
|
||||
//
|
||||
// The file comes as the raw request body, not as a form: the access token is a header,
|
||||
// not a cookie, so there is no cross-site form to guard against, and a raw body needs
|
||||
// no antiforgery exception. The source license key travels in a header and never in
|
||||
// the address, where it would end up in logs.
|
||||
verwaltung.MapPost("/backups/import", async (
|
||||
HttpContext context, ManagerBackups backups, CancellationToken ct) =>
|
||||
{
|
||||
var name = context.Request.Query["name"].ToString();
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
return Results.BadRequest(new OutcomeDto(false, "The new instance needs a name."));
|
||||
|
||||
if (context.Features.Get<Microsoft.AspNetCore.Http.Features.IHttpMaxRequestBodySizeFeature>() is { IsReadOnly: false } limit)
|
||||
limit.MaxRequestBodySize = MaxImportBytes;
|
||||
|
||||
var upload = Path.Combine(Path.GetTempPath(), "clawd-import-" + Guid.NewGuid().ToString("N") + ".bin");
|
||||
try
|
||||
{
|
||||
await using (var file = File.Create(upload))
|
||||
await context.Request.Body.CopyToAsync(file, ct);
|
||||
|
||||
if (new FileInfo(upload).Length == 0)
|
||||
return Results.BadRequest(new OutcomeDto(false, "The upload was empty."));
|
||||
|
||||
var result = await backups.ImportAsNewInstanceAsync(
|
||||
upload, name,
|
||||
context.Request.Headers["X-Source-License-Key"].ToString(),
|
||||
context.Request.Headers["X-Backup-Passphrase"].ToString(),
|
||||
CancellationToken.None);
|
||||
|
||||
if (result.KeyNeeded)
|
||||
return Results.Json(new KeyNeededDto(false, result.Message, "fremde_lizenz"),
|
||||
statusCode: StatusCodes.Status422UnprocessableEntity);
|
||||
|
||||
return result.Ok
|
||||
? Results.Ok(new OutcomeDto(true, result.Message))
|
||||
: Results.BadRequest(new OutcomeDto(false, result.Message));
|
||||
}
|
||||
catch (Microsoft.AspNetCore.Http.BadHttpRequestException ex) when (ex.StatusCode == StatusCodes.Status413PayloadTooLarge)
|
||||
{
|
||||
return Results.Json(new OutcomeDto(false, $"The backup is larger than {MaxImportBytes / (1024 * 1024 * 1024)} GB."),
|
||||
statusCode: StatusCodes.Status413PayloadTooLarge);
|
||||
}
|
||||
finally
|
||||
{
|
||||
try { File.Delete(upload); }
|
||||
catch (IOException) { /* temp file; the next cleanup of the temp folder takes it */ }
|
||||
}
|
||||
});
|
||||
|
||||
// ─── License and manager settings (#764) ───
|
||||
|
||||
verwaltung.MapGet("/license", (ManagerCore manager) =>
|
||||
|
||||
Reference in New Issue
Block a user