diff options
Diffstat (limited to 'src/marlin.web/Controllers')
| -rw-r--r-- | src/marlin.web/Controllers/JobsController.cs | 31 | ||||
| -rw-r--r-- | src/marlin.web/Controllers/VCSController.cs | 92 |
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 |
