Forge
csharp3407750f
1using AgentForge;
2using AgentForge.Abstractions;
3using AgentForge.Contracts;
4using AgentForge.Data;
5using AgentForge.Data.Entities;
6using AgentForge.Plugin.Sdk;
7using AgentForge.Plugin.View;
8using AgentForge.Plugin.Org.Web.Components;
9using AgentForge.Plugin.Org.Web.Pages;
10using AgentForge.Plugin.Org.Web.Services;
11using AgentForge.Services;
12using Microsoft.AspNetCore.Mvc;
13
14namespace AgentForge.Plugin.Org.Web;
15
16internal static class ForgeOrgViewEndpoints
17{
18 internal static void Map(WebApplication app)
19 {
20 app.MapGet("/view/me", RenderPersonalView);
21 app.MapGet("/view/orgs/{slug}", RenderOrgView);
22 }
23
24 private static IResult RenderPersonalView(
25 string? group,
26 string? groupStack,
27 string? groupStackRows,
28 ForgeRepository repository,
29 GitRepoService git,
30 ForgeRepoAccessService access,
31 HttpContext http,
32 [FromServices] IForgeOrgCatalogGrouping? grouping = null)
33 {
34 var identity = ForgeViewIdentityResolver.TryResolve(http, access)
35 ?? throw new ForgeApiException(401, "Sign in required.");
36
37 var personalOrg = new ForgePersonalOrgService(repository, git).EnsurePersonalOrg(identity);
38 return RenderCatalogView(
39 personalOrg,
40 "/view/me",
41 personalOrg.DisplayName,
42 group,
43 groupStack,
44 groupStackRows,
45 repository,
46 git,
47 access,
48 http,
49 grouping,
50 body => ForgePersonalHomePage.Render(
51 identity,
52 personalOrg,
53 body.Groups,
54 body.Repos,
55 body.GroupPaths,
56 body.GroupingContext,
57 grouping));
58 }
59
60 private static IResult RenderOrgView(
61 string slug,
62 string? group,
63 string? groupStack,
64 string? groupStackRows,
65 ForgeRepository repository,
66 GitRepoService git,
67 ForgeRepoAccessService access,
68 HttpContext http,
69 [FromServices] IForgeOrgCatalogGrouping? grouping = null)
70 {
71 var org = repository.GetOrgBySlug(slug)
72 ?? throw new ForgeApiException(404, $"Organization '{slug}' not found.");
73
74 return RenderCatalogView(
75 org,
76 $"/view/orgs/{Uri.EscapeDataString(org.Slug)}",
77 org.DisplayName,
78 group,
79 groupStack,
80 groupStackRows,
81 repository,
82 git,
83 access,
84 http,
85 grouping,
86 body => ForgeOrgHomePage.Render(
87 org,
88 body.Groups,
89 body.Repos,
90 body.GroupPaths,
91 body.GroupingContext,
92 grouping));
93 }
94
95 private static IResult RenderCatalogView(
96 ForgeOrgEntity org,
97 string catalogBasePath,
98 string pageTitle,
99 string? group,
100 string? groupStackQuery,
101 string? groupStackRowsQuery,
102 ForgeRepository repository,
103 GitRepoService git,
104 ForgeRepoAccessService access,
105 HttpContext http,
106 IForgeOrgCatalogGrouping? grouping,
107 Func<CatalogBody, string> renderBody)
108 {
109 var principal = access.Resolve(http);
110 var groups = repository.ListRepoGroups(org.Id);
111 var groupPaths = repository.BuildRepoGroupPathLookup(org.Id);
112 var repos = access.FilterReadable(repository.ListRepos(orgSlug: org.Slug), principal)
113 .Select(repo => ForgePluginResponses.ToRepoResponse(repo, git, repository))
114 .ToList();
115
116 var activeGroupPath = string.IsNullOrWhiteSpace(group)
117 ? null
118 : ForgeRepoGroupPath.NormalizeRoute(group);
119 var stack = grouping?.ParseGroupStack(groupStackQuery) ?? [];
120 var editorRowCount = grouping?.ParseEditorRowCount(groupStackRowsQuery, stack.Count) ?? 0;
121 var groupingContext = new ForgeOrgCatalogViewContext(catalogBasePath, activeGroupPath, stack, editorRowCount);
122
123 var body = renderBody(new CatalogBody(groups, repos, groupPaths, groupingContext));
124 return ForgeViewPresenter.Page(pageTitle, body, http);
125 }
126
127 private readonly record struct CatalogBody(
128 IReadOnlyList<ForgeRepoGroupEntity> Groups,
129 IReadOnlyList<RepoResponse> Repos,
130 IReadOnlyDictionary<string, string> GroupPaths,
131 ForgeOrgCatalogViewContext GroupingContext);
132}
133
View only · write via MCP/CIDE