mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-03 18:11:36 -05:00
A server Auto Nest result could only be reviewed in the session that sent it, and nothing stopped two desktops from keeping the same result. Phase 3 adds My Server Jobs, so results that finished while OpenNest was closed are easy to find, and records each review on the server. - Server: GET /api/nest-jobs lists jobs newest first with state, engine, submitter, times, queue position, review and the worker's summary (parts, sheets, utilization, unplaced). It filters to active, finished or unreviewed jobs and to a submitter (a bare user name matches that user on any workstation). An ETag turns unchanged polls into 304s. Schema v17 adds nestId, review and summary columns, only where missing. - Keep once: POST .../keep and .../discard take If-Match on the job revision and change an unreviewed result only once. A second desktop gets 409 with who kept it. DELETE removes finished jobs. A succeeded job keeps its package until it is reviewed, so a later session can rebuild the job. - Retention: OPENNEST_JOB_RETENTION_DAYS (default 7) deletes finished jobs with their results. It runs on claims and list reads. A malformed value fails startup instead of deleting results early. - Worker: reports the result's summary with the transcript. A job cancelled while its result arrived keeps no summary (found in the Docker smoke run). - Desktop: Nest > My Server Jobs polls the list every 3 s; polling rather than per-job SSE because the list must show other desktops' jobs and removals. Open rebuilds the job from job.json against the open nest's own drawings. New per-drawing geometry fingerprints make the freshness check work across sessions. Cancel Job and Remove act on the selected job. A status-bar item counts this user's results waiting for review; there is no popup. Auto Nest's review now records Keep on the server before it commits, and records Discard too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
72 lines
3.2 KiB
C#
72 lines
3.2 KiB
C#
namespace OpenNest.Server;
|
|
|
|
/// <summary>Startup settings read from the command line and environment.</summary>
|
|
public static class ServerOptions
|
|
{
|
|
public const string DatabaseVariable = "OPENNEST_DB";
|
|
public const string UpdatesVariable = "OPENNEST_UPDATES";
|
|
public const string JobRetentionVariable = "OPENNEST_JOB_RETENTION_DAYS";
|
|
|
|
/// <summary>
|
|
/// How long finished server nest jobs are kept: <c>OPENNEST_JOB_RETENTION_DAYS</c> (a positive
|
|
/// number of days, fractions allowed), or <see cref="Data.NestJobRules.Retention"/> when unset.
|
|
/// Anything else fails startup rather than deleting results sooner than intended.
|
|
/// </summary>
|
|
public static TimeSpan JobRetention(string? value)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
return Data.NestJobRules.Retention;
|
|
if (!double.TryParse(value.Trim(), System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out var days)
|
|
|| !(days > 0) || days > 36500)
|
|
throw new ArgumentException($"{JobRetentionVariable} must be a positive number of days.", nameof(value));
|
|
return TimeSpan.FromDays(days);
|
|
}
|
|
|
|
/// <summary>
|
|
/// True unless <c>OPENNEST_UPDATES</c> is <c>off</c>: the server mirrors the desktop
|
|
/// app's update feed from GitHub (see <see cref="UpdateMirror"/>).
|
|
/// </summary>
|
|
public static bool UpdatesEnabled(string? value) =>
|
|
!string.Equals(value?.Trim(), "off", StringComparison.OrdinalIgnoreCase);
|
|
|
|
/// <summary>The mirrored update feed lives in an <c>updates</c> directory beside the database.</summary>
|
|
public static string UpdatesPath(string databasePath) =>
|
|
Path.Combine(Path.GetDirectoryName(Path.GetFullPath(databasePath))!, "updates");
|
|
|
|
/// <summary>
|
|
/// The database path: <c>--database=<path></c> or <c>--database <path></c>, then
|
|
/// <c>OPENNEST_DB</c>, then <c>./data/nests.db</c>. A <c>--database</c> without a path,
|
|
/// or given twice, fails startup instead of silently storing nests elsewhere.
|
|
/// </summary>
|
|
public static string DatabasePath(IReadOnlyList<string> args, string? environmentPath)
|
|
{
|
|
string? path = null;
|
|
for (var i = 0; i < args.Count; i++)
|
|
{
|
|
string value;
|
|
if (args[i] == "--database")
|
|
{
|
|
if (i + 1 >= args.Count || args[i + 1].StartsWith("--", StringComparison.Ordinal))
|
|
throw new ArgumentException("--database requires a path.", nameof(args));
|
|
value = args[++i];
|
|
}
|
|
else if (args[i].StartsWith("--database=", StringComparison.Ordinal))
|
|
{
|
|
value = args[i]["--database=".Length..];
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(value))
|
|
throw new ArgumentException("--database requires a path.", nameof(args));
|
|
if (path is not null)
|
|
throw new ArgumentException("--database was given more than once.", nameof(args));
|
|
path = value;
|
|
}
|
|
|
|
return path ?? environmentPath ?? Path.Combine("data", "nests.db");
|
|
}
|
|
}
|