summaryrefslogtreecommitdiff
path: root/src/marlin.web/Controllers
diff options
context:
space:
mode:
authorMelodi <62381002+Melodi17@users.noreply.github.com>2026-09-06 09:02:31 +1000
committerMelodi <62381002+Melodi17@users.noreply.github.com>2026-09-06 09:02:31 +1000
commita9bc03fb01b5d1ac41bf5592f384d22df5425aa4 (patch)
tree16ae13e19d2a3cd7488202c30790d09557559d70 /src/marlin.web/Controllers
parentc00992ffad24212c4398736efd6d707d4cb0cc0d (diff)
feat: vcs and secret management support
Diffstat (limited to 'src/marlin.web/Controllers')
-rw-r--r--src/marlin.web/Controllers/JobsController.cs31
-rw-r--r--src/marlin.web/Controllers/VCSController.cs92
2 files changed, 119 insertions, 4 deletions
diff --git a/src/marlin.web/Controllers/JobsController.cs b/src/marlin.web/Controllers/JobsController.cs
index 1cfdadb..c86cb2d 100644
--- a/src/marlin.web/Controllers/JobsController.cs
+++ b/src/marlin.web/Controllers/JobsController.cs
@@ -30,10 +30,23 @@ public class JobsController(ApplicationDbContext dbContext) : Controller
return this.View(results);
}
+ public record JobSpawnDetails(
+ ICollection<VersionControlSource> Sources,
+ Guid? Vcs = null,
+ string? Ref = null,
+ string? Commit = null,
+ string? Recipe = null);
+
[HttpGet("spawn")]
- public async Task<IActionResult> Spawn()
+ public async Task<IActionResult> Spawn(CancellationToken ct)
{
- return this.View();
+ var sources = await dbContext.Sources
+ .Include(x => x.Branches)
+ .ThenInclude(x => x.Commit)
+ .ToListAsync(ct);
+
+ var details = new JobSpawnDetails(sources);
+ return this.View(details);
}
public record JobSpawnRequest(Guid Vcs, string Ref, string Commit, string Recipe);
@@ -62,4 +75,18 @@ public class JobsController(ApplicationDbContext dbContext) : Controller
// cancel job
// details job (recipe, status, agent, target, plan, log, artifacts)
+ [HttpGet("details/{id}")]
+ public async Task<IActionResult> Details(Guid id)
+ {
+ var job = await dbContext.Jobs
+ .Include(x => x.Agent)
+ .Include(x => x.Source)
+ .Include(x => x.StatusUpdates)
+ .FirstOrDefaultAsync(x => x.Id == id);
+
+ if (job == null)
+ return this.NotFound();
+
+ return this.View(job);
+ }
} \ No newline at end of file
diff --git a/src/marlin.web/Controllers/VCSController.cs b/src/marlin.web/Controllers/VCSController.cs
index 86f655b..7370e0d 100644
--- a/src/marlin.web/Controllers/VCSController.cs
+++ b/src/marlin.web/Controllers/VCSController.cs
@@ -1,33 +1,47 @@
namespace marlin.web.Controllers;
+using System.Text.Json;
using core;
using core.models;
using Data;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
+using Models;
+using Services;
[Route("vcs")]
-public class VCSController(ApplicationDbContext dbContext) : Controller
+public class VCSController(ApplicationDbContext dbContext, VersionControlSyncService syncService) : Controller
{
[HttpGet("list")]
public async Task<IActionResult> List(CancellationToken ct)
{
var items = await dbContext.Sources
+ .Include(x => x.Branches)
.ToListAsync(ct);
return this.View(items);
}
+ public record SecretOption(Guid Id, string Label);
+
[HttpGet("details/{id}")]
public async Task<IActionResult> Details(Guid id, CancellationToken ct)
{
var item = await dbContext.Sources
+ .Include(x => x.Branches)
+ .ThenInclude(x => x.Commit)
.FirstOrDefaultAsync(x => x.Id == id, ct);
if (item == null)
return this.NotFound();
- return this.View(item);
+ // Only Id/Label projected — a secret's Content never needs to leave the secrets pages.
+ var secretOptions = await dbContext.Secrets
+ .Where(x => x.SourceId == id)
+ .Select(x => new SecretOption(x.Id, x.Label))
+ .ToListAsync(ct);
+
+ return this.View((item, secretOptions));
}
[HttpPost("edit/{id}")]
@@ -43,8 +57,10 @@ public class VCSController(ApplicationDbContext dbContext) : Controller
item.BranchFilter = vcs.BranchFilter;
item.MainBranch = vcs.MainBranch;
item.MinPollingInterval = vcs.MinPollingInterval;
+ item.AccessSecretId = vcs.AccessSecretId;
await dbContext.SaveChangesAsync(ct);
+ await syncService.FetchSources(ct);
return this.RedirectToAction("List");
}
@@ -62,7 +78,79 @@ public class VCSController(ApplicationDbContext dbContext) : Controller
await dbContext.AddAsync(vcs, ct);
await dbContext.SaveChangesAsync(ct);
+ await syncService.FetchSources(ct);
return this.RedirectToAction("Details", new { id = vcs.Id });
}
+
+ public record SecretCreateRequest(string Label, string? Kind, bool MayReveal, string ContentJson);
+
+ [HttpGet("details/{sourceId}/secrets/create")]
+ public async Task<IActionResult> CreateSecret(Guid sourceId, CancellationToken ct)
+ {
+ var source = await dbContext.Sources
+ .FirstOrDefaultAsync(x => x.Id == sourceId, ct);
+
+ if (source == null)
+ return this.NotFound();
+
+ return this.View(source);
+ }
+
+ [HttpPost("details/{sourceId}/secrets/create")]
+ public async Task<IActionResult> CreateSecretPost(Guid sourceId, SecretCreateRequest request, CancellationToken ct)
+ {
+ var source = await dbContext.Sources
+ .FirstOrDefaultAsync(x => x.Id == sourceId, ct);
+
+ if (source == null)
+ return this.NotFound();
+
+ var content = JsonSerializer.Deserialize<Dictionary<string, string>>(request.ContentJson) ?? new();
+
+ var secret = new VersionControlSecret
+ {
+ SourceId = sourceId,
+ Label = request.Label,
+ Kind = request.Kind,
+ MayReveal = request.MayReveal,
+ Content = content
+ };
+
+ await dbContext.Secrets.AddAsync(secret, ct);
+ await dbContext.SaveChangesAsync(ct);
+
+ return this.RedirectToAction("ListSecret", new { sourceId });
+ }
+
+ [HttpGet("details/{sourceId}/secrets/list")]
+ public async Task<IActionResult> ListSecret(Guid sourceId, CancellationToken ct)
+ {
+ var source = await dbContext.Sources
+ .FirstOrDefaultAsync(x => x.Id == sourceId, ct);
+
+ if (source == null)
+ return this.NotFound();
+
+ var items = await dbContext.Secrets
+ .Where(x => x.SourceId == source.Id)
+ .ToListAsync<ISecret>(ct);
+
+ return this.View((source, items));
+ }
+
+ [HttpPost("details/{sourceId}/secrets/delete/{id}")]
+ public async Task<IActionResult> DeleteSecret(Guid sourceId, Guid id, CancellationToken ct)
+ {
+ var secret = await dbContext.Secrets
+ .FirstOrDefaultAsync(x => x.SourceId == sourceId && x.Id == id, ct);
+
+ if (secret == null)
+ return this.NotFound();
+
+ dbContext.Secrets.Remove(secret);
+ await dbContext.SaveChangesAsync(ct);
+
+ return this.RedirectToAction("ListSecret", new { sourceId = sourceId });
+ }
} \ No newline at end of file