Skip to content

Commit 00bf6c8

Browse files
committed
Refactor: Make API client-agnostic by removing Format from Project entity
- Remove Format, LocalizationPath, ConfigJson, ConfigVersion, ConfigUpdatedAt, ConfigUpdatedBy from Project entity - Add GitHubFormat field for GitHub operations only - Create GitHubFormatResolver service for auto-detecting format (priority: explicit > lrm.json > file detection) - Add Source field to SyncHistory to track sync origin (cli, web-edit, github) - Update all services, controllers, DTOs, and UI components - Remove configuration sync endpoints from controllers - Simplify project creation to just name + defaultLanguage
1 parent cd7c8d3 commit 00bf6c8

46 files changed

Lines changed: 5343 additions & 1041 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cloud/src/LrmCloud.Api/Controllers/OrgProjectsController.cs

Lines changed: 0 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -50,64 +50,6 @@ public async Task<ActionResult<ApiResponse<ProjectDto>>> GetProject(string orgSl
5050
return Success(project);
5151
}
5252

53-
/// <summary>
54-
/// Gets the project configuration (lrm.json).
55-
/// </summary>
56-
[HttpGet("configuration")]
57-
[ProducesResponseType(typeof(ApiResponse<ConfigurationDto>), 200)]
58-
[ProducesResponseType(typeof(ProblemDetails), 404)]
59-
public async Task<ActionResult<ApiResponse<ConfigurationDto>>> GetConfiguration(string orgSlug, string projectSlug)
60-
{
61-
var userId = int.Parse(User.FindFirst(ClaimTypes.NameIdentifier)!.Value);
62-
var project = await _projectService.GetProjectByOrgSlugAsync(orgSlug, projectSlug, userId);
63-
64-
if (project == null)
65-
return NotFound("PRJ_NOT_FOUND", "Project not found or access denied");
66-
67-
var configuration = await _projectService.GetConfigurationAsync(project.Id, userId);
68-
69-
if (configuration == null)
70-
return NotFound("CFG_NOT_FOUND", "Configuration not found");
71-
72-
return Success(configuration);
73-
}
74-
75-
/// <summary>
76-
/// Updates the project configuration (lrm.json).
77-
/// </summary>
78-
[HttpPut("configuration")]
79-
[ProducesResponseType(typeof(ApiResponse<ConfigurationDto>), 200)]
80-
[ProducesResponseType(typeof(ProblemDetails), 400)]
81-
[ProducesResponseType(typeof(ProblemDetails), 404)]
82-
[ProducesResponseType(typeof(ProblemDetails), 409)]
83-
public async Task<ActionResult<ApiResponse<ConfigurationDto>>> UpdateConfiguration(
84-
string orgSlug,
85-
string projectSlug,
86-
[FromBody] UpdateConfigurationRequest request)
87-
{
88-
var userId = int.Parse(User.FindFirst(ClaimTypes.NameIdentifier)!.Value);
89-
var project = await _projectService.GetProjectByOrgSlugAsync(orgSlug, projectSlug, userId);
90-
91-
if (project == null)
92-
return NotFound("PRJ_NOT_FOUND", "Project not found or access denied");
93-
94-
var (success, configuration, errorMessage) = await _projectService.UpdateConfigurationAsync(project.Id, userId, request);
95-
96-
if (!success)
97-
{
98-
if (errorMessage == "Configuration not found")
99-
return NotFound("CFG_NOT_FOUND", errorMessage);
100-
101-
if (errorMessage == "Configuration conflict")
102-
return Conflict("CFG_CONFLICT", "Configuration has been modified by another user. Please pull and merge changes.");
103-
104-
return BadRequest("CFG_UPDATE_FAILED", errorMessage!);
105-
}
106-
107-
_logger.LogInformation("User {UserId} updated configuration for project {ProjectId} via org/project route", userId, project.Id);
108-
return Success(configuration!);
109-
}
110-
11153
/// <summary>
11254
/// Gets all resources for the project.
11355
/// </summary>
@@ -151,28 +93,4 @@ public async Task<ActionResult<ApiResponse<SyncStatusDto>>> GetSyncStatus(string
15193
return Success(status);
15294
}
15395

154-
/// <summary>
155-
/// Gets configuration history.
156-
/// </summary>
157-
[HttpGet("configuration/history")]
158-
[ProducesResponseType(typeof(ApiResponse<List<ConfigurationHistoryDto>>), 200)]
159-
[ProducesResponseType(typeof(ProblemDetails), 404)]
160-
public async Task<ActionResult<ApiResponse<List<ConfigurationHistoryDto>>>> GetConfigurationHistory(
161-
string orgSlug,
162-
string projectSlug,
163-
[FromQuery] int limit = 50)
164-
{
165-
var userId = int.Parse(User.FindFirst(ClaimTypes.NameIdentifier)!.Value);
166-
var project = await _projectService.GetProjectByOrgSlugAsync(orgSlug, projectSlug, userId);
167-
168-
if (project == null)
169-
return NotFound("PRJ_NOT_FOUND", "Project not found or access denied");
170-
171-
var history = await _projectService.GetConfigurationHistoryAsync(project.Id, userId, limit);
172-
173-
if (history == null)
174-
return NotFound("PRJ_NOT_FOUND", "Project not found or access denied");
175-
176-
return Success(history);
177-
}
17896
}

cloud/src/LrmCloud.Api/Controllers/ProjectsController.cs

Lines changed: 0 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -189,78 +189,6 @@ public async Task<ActionResult<ApiResponse>> TriggerSync(int id)
189189
return Success(errorMessage ?? "Sync triggered successfully");
190190
}
191191

192-
/// <summary>
193-
/// Gets the project configuration (lrm.json).
194-
/// </summary>
195-
/// <param name="id">Project ID</param>
196-
/// <returns>Project configuration</returns>
197-
[HttpGet("{id}/configuration")]
198-
[ProducesResponseType(typeof(ApiResponse<ConfigurationDto>), 200)]
199-
[ProducesResponseType(typeof(ProblemDetails), 404)]
200-
public async Task<ActionResult<ApiResponse<ConfigurationDto>>> GetConfiguration(int id)
201-
{
202-
var userId = int.Parse(User.FindFirst(ClaimTypes.NameIdentifier)!.Value);
203-
var configuration = await _projectService.GetConfigurationAsync(id, userId);
204-
205-
if (configuration == null)
206-
return NotFound("CFG_NOT_FOUND", "Configuration not found or access denied");
207-
208-
return Success(configuration);
209-
}
210-
211-
/// <summary>
212-
/// Updates the project configuration (lrm.json).
213-
/// </summary>
214-
/// <param name="id">Project ID</param>
215-
/// <param name="request">Configuration update request</param>
216-
/// <returns>Updated configuration</returns>
217-
[HttpPut("{id}/configuration")]
218-
[ProducesResponseType(typeof(ApiResponse<ConfigurationDto>), 200)]
219-
[ProducesResponseType(typeof(ProblemDetails), 400)]
220-
[ProducesResponseType(typeof(ProblemDetails), 404)]
221-
[ProducesResponseType(typeof(ProblemDetails), 409)]
222-
public async Task<ActionResult<ApiResponse<ConfigurationDto>>> UpdateConfiguration(
223-
int id, [FromBody] UpdateConfigurationRequest request)
224-
{
225-
var userId = int.Parse(User.FindFirst(ClaimTypes.NameIdentifier)!.Value);
226-
var (success, configuration, errorMessage) = await _projectService.UpdateConfigurationAsync(id, userId, request);
227-
228-
if (!success)
229-
{
230-
if (errorMessage == "Configuration not found")
231-
return NotFound("CFG_NOT_FOUND", errorMessage);
232-
233-
if (errorMessage == "Configuration conflict")
234-
return Conflict("CFG_CONFLICT", "Configuration has been modified by another user. Please pull and merge changes.");
235-
236-
return BadRequest("CFG_UPDATE_FAILED", errorMessage!);
237-
}
238-
239-
_logger.LogInformation("User {UserId} updated configuration for project {ProjectId}", userId, id);
240-
return Success(configuration!);
241-
}
242-
243-
/// <summary>
244-
/// Gets the configuration history for a project.
245-
/// </summary>
246-
/// <param name="id">Project ID</param>
247-
/// <param name="limit">Maximum number of history entries to return</param>
248-
/// <returns>Configuration history</returns>
249-
[HttpGet("{id}/configuration/history")]
250-
[ProducesResponseType(typeof(ApiResponse<List<ConfigurationHistoryDto>>), 200)]
251-
[ProducesResponseType(typeof(ProblemDetails), 404)]
252-
public async Task<ActionResult<ApiResponse<List<ConfigurationHistoryDto>>>> GetConfigurationHistory(
253-
int id, [FromQuery] int limit = 50)
254-
{
255-
var userId = int.Parse(User.FindFirst(ClaimTypes.NameIdentifier)!.Value);
256-
var history = await _projectService.GetConfigurationHistoryAsync(id, userId, limit);
257-
258-
if (history == null)
259-
return NotFound("PRJ_NOT_FOUND", "Project not found or access denied");
260-
261-
return Success(history);
262-
}
263-
264192
/// <summary>
265193
/// Gets the sync status for a project.
266194
/// </summary>

cloud/src/LrmCloud.Api/Controllers/SyncController.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ public async Task<ActionResult<ApiResponse<RevertResponse>>> Revert(
267267
{
268268
HistoryId = revertHistory.HistoryId,
269269
OperationType = revertHistory.OperationType,
270+
Source = revertHistory.Source,
270271
Message = revertHistory.Message,
271272
EntriesAdded = revertHistory.EntriesAdded,
272273
EntriesModified = revertHistory.EntriesModified,

cloud/src/LrmCloud.Api/Controllers/UserProjectsController.cs

Lines changed: 0 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -47,64 +47,6 @@ public async Task<ActionResult<ApiResponse<ProjectDto>>> GetProject(string usern
4747
return Success(project);
4848
}
4949

50-
/// <summary>
51-
/// Gets the project configuration (lrm.json).
52-
/// </summary>
53-
[HttpGet("configuration")]
54-
[ProducesResponseType(typeof(ApiResponse<ConfigurationDto>), 200)]
55-
[ProducesResponseType(typeof(ProblemDetails), 404)]
56-
public async Task<ActionResult<ApiResponse<ConfigurationDto>>> GetConfiguration(string username, string projectName)
57-
{
58-
var userId = int.Parse(User.FindFirst(ClaimTypes.NameIdentifier)!.Value);
59-
var project = await _projectService.GetProjectByNameAsync(username, projectName, userId);
60-
61-
if (project == null)
62-
return NotFound("PRJ_NOT_FOUND", "Project not found or access denied");
63-
64-
var configuration = await _projectService.GetConfigurationAsync(project.Id, userId);
65-
66-
if (configuration == null)
67-
return NotFound("CFG_NOT_FOUND", "Configuration not found");
68-
69-
return Success(configuration);
70-
}
71-
72-
/// <summary>
73-
/// Updates the project configuration (lrm.json).
74-
/// </summary>
75-
[HttpPut("configuration")]
76-
[ProducesResponseType(typeof(ApiResponse<ConfigurationDto>), 200)]
77-
[ProducesResponseType(typeof(ProblemDetails), 400)]
78-
[ProducesResponseType(typeof(ProblemDetails), 404)]
79-
[ProducesResponseType(typeof(ProblemDetails), 409)]
80-
public async Task<ActionResult<ApiResponse<ConfigurationDto>>> UpdateConfiguration(
81-
string username,
82-
string projectName,
83-
[FromBody] UpdateConfigurationRequest request)
84-
{
85-
var userId = int.Parse(User.FindFirst(ClaimTypes.NameIdentifier)!.Value);
86-
var project = await _projectService.GetProjectByNameAsync(username, projectName, userId);
87-
88-
if (project == null)
89-
return NotFound("PRJ_NOT_FOUND", "Project not found or access denied");
90-
91-
var (success, configuration, errorMessage) = await _projectService.UpdateConfigurationAsync(project.Id, userId, request);
92-
93-
if (!success)
94-
{
95-
if (errorMessage == "Configuration not found")
96-
return NotFound("CFG_NOT_FOUND", errorMessage);
97-
98-
if (errorMessage == "Configuration conflict")
99-
return Conflict("CFG_CONFLICT", "Configuration has been modified by another user. Please pull and merge changes.");
100-
101-
return BadRequest("CFG_UPDATE_FAILED", errorMessage!);
102-
}
103-
104-
_logger.LogInformation("User {UserId} updated configuration for project {ProjectId} via username/projectName route", userId, project.Id);
105-
return Success(configuration!);
106-
}
107-
10850
/// <summary>
10951
/// Gets all resources for the project.
11052
/// </summary>
@@ -148,28 +90,4 @@ public async Task<ActionResult<ApiResponse<SyncStatusDto>>> GetSyncStatus(string
14890
return Success(status);
14991
}
15092

151-
/// <summary>
152-
/// Gets configuration history.
153-
/// </summary>
154-
[HttpGet("configuration/history")]
155-
[ProducesResponseType(typeof(ApiResponse<List<ConfigurationHistoryDto>>), 200)]
156-
[ProducesResponseType(typeof(ProblemDetails), 404)]
157-
public async Task<ActionResult<ApiResponse<List<ConfigurationHistoryDto>>>> GetConfigurationHistory(
158-
string username,
159-
string projectName,
160-
[FromQuery] int limit = 50)
161-
{
162-
var userId = int.Parse(User.FindFirst(ClaimTypes.NameIdentifier)!.Value);
163-
var project = await _projectService.GetProjectByNameAsync(username, projectName, userId);
164-
165-
if (project == null)
166-
return NotFound("PRJ_NOT_FOUND", "Project not found or access denied");
167-
168-
var history = await _projectService.GetConfigurationHistoryAsync(project.Id, userId, limit);
169-
170-
if (history == null)
171-
return NotFound("PRJ_NOT_FOUND", "Project not found or access denied");
172-
173-
return Success(history);
174-
}
17593
}

cloud/src/LrmCloud.Api/Data/AppDbContext.cs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,6 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
174174
entity.HasIndex(e => e.UserId);
175175
entity.HasIndex(e => e.OrganizationId);
176176
entity.HasIndex(e => e.GitHubRepo);
177-
entity.HasIndex(e => e.ConfigUpdatedBy);
178177

179178
// Unique slug per user (for personal projects)
180179
entity.HasIndex(e => new { e.UserId, e.Slug })
@@ -186,13 +185,6 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
186185
.HasFilter("organization_id IS NOT NULL")
187186
.IsUnique();
188187

189-
// Configure ConfigUpdater relationship (who last updated config)
190-
// Note: User relationship is already configured via [ForeignKey] attribute
191-
entity.HasOne(e => e.ConfigUpdater)
192-
.WithMany()
193-
.HasForeignKey(e => e.ConfigUpdatedBy)
194-
.OnDelete(DeleteBehavior.SetNull);
195-
196188
// Configure GitHubConnectedByUser relationship (who connected GitHub)
197189
entity.HasOne(e => e.GitHubConnectedByUser)
198190
.WithMany()

0 commit comments

Comments
 (0)