Skip to content

Commit 29da661

Browse files
committed
- init code
1 parent a4a01b4 commit 29da661

File tree

73 files changed

+3005
-107
lines changed

Some content is hidden

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

73 files changed

+3005
-107
lines changed

.dockerignore

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
**/.classpath
2+
**/.dockerignore
3+
**/.env
4+
**/.git
5+
**/.gitignore
6+
**/.project
7+
**/.settings
8+
**/.toolstarget
9+
**/.vs
10+
**/.vscode
11+
**/*.*proj.user
12+
**/*.dbmdl
13+
**/*.jfm
14+
**/azds.yaml
15+
**/bin
16+
**/charts
17+
**/docker-compose*
18+
**/Dockerfile*
19+
**/node_modules
20+
**/npm-debug.log
21+
**/obj
22+
**/secrets.dev.yaml
23+
**/values.dev.yaml
24+
LICENSE
25+
README.md
26+
!**/.gitignore
27+
!.git/HEAD
28+
!.git/config
29+
!.git/packed-refs
30+
!.git/refs/heads/**

MyLibrary.sln

Lines changed: 0 additions & 55 deletions
This file was deleted.

eAuthor.sln

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.14.36408.4
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
7+
EndProject
8+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{4A4020B9-AF72-4689-9D92-7DBD1C6066F0}"
9+
EndProject
10+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "eAuthor.API", "src\eAuthor.API\eAuthor.API.csproj", "{B0185923-2D65-8FD9-65F7-FA199AC80DED}"
11+
EndProject
12+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "eAuthor.API.Tests", "tests\eAuthor.API.Tests\eAuthor.API.Tests.csproj", "{1D6A62AF-3292-EC75-EE61-15F38017F890}"
13+
EndProject
14+
Global
15+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
16+
Debug|Any CPU = Debug|Any CPU
17+
Release|Any CPU = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
20+
{B0185923-2D65-8FD9-65F7-FA199AC80DED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21+
{B0185923-2D65-8FD9-65F7-FA199AC80DED}.Debug|Any CPU.Build.0 = Debug|Any CPU
22+
{B0185923-2D65-8FD9-65F7-FA199AC80DED}.Release|Any CPU.ActiveCfg = Release|Any CPU
23+
{B0185923-2D65-8FD9-65F7-FA199AC80DED}.Release|Any CPU.Build.0 = Release|Any CPU
24+
{1D6A62AF-3292-EC75-EE61-15F38017F890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25+
{1D6A62AF-3292-EC75-EE61-15F38017F890}.Debug|Any CPU.Build.0 = Debug|Any CPU
26+
{1D6A62AF-3292-EC75-EE61-15F38017F890}.Release|Any CPU.ActiveCfg = Release|Any CPU
27+
{1D6A62AF-3292-EC75-EE61-15F38017F890}.Release|Any CPU.Build.0 = Release|Any CPU
28+
EndGlobalSection
29+
GlobalSection(SolutionProperties) = preSolution
30+
HideSolutionNode = FALSE
31+
EndGlobalSection
32+
GlobalSection(NestedProjects) = preSolution
33+
{B0185923-2D65-8FD9-65F7-FA199AC80DED} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
34+
{1D6A62AF-3292-EC75-EE61-15F38017F890} = {4A4020B9-AF72-4689-9D92-7DBD1C6066F0}
35+
EndGlobalSection
36+
GlobalSection(ExtensibilityGlobals) = postSolution
37+
SolutionGuid = {7F0AD491-F8FC-406E-9730-763ED379A15C}
38+
EndGlobalSection
39+
EndGlobal

src/MyLibrary/Class1.cs

Lines changed: 0 additions & 6 deletions
This file was deleted.

src/MyLibrary/MyLibrary.csproj

Lines changed: 0 additions & 9 deletions
This file was deleted.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using eAuthor.Models;
2+
using eAuthor.Repositories;
3+
using Microsoft.AspNetCore.Authorization;
4+
using Microsoft.AspNetCore.Mvc;
5+
6+
namespace eAuthor.Controllers;
7+
8+
[ApiController]
9+
[Route("api/[controller]")]
10+
[Authorize]
11+
public class BaseDocxTemplatesController : ControllerBase
12+
{
13+
private readonly IBaseDocxTemplateRepository _repo;
14+
15+
public BaseDocxTemplatesController(IBaseDocxTemplateRepository repo)
16+
{
17+
_repo = repo;
18+
}
19+
20+
[HttpPost("upload")]
21+
[RequestSizeLimit(20_000_000)] // 20MB
22+
public async Task<IActionResult> Upload([FromForm] IFormFile file, [FromForm] string name)
23+
{
24+
if (file == null || file.Length == 0)
25+
return BadRequest("File required");
26+
using var ms = new MemoryStream();
27+
await file.CopyToAsync(ms);
28+
var template = new BaseDocxTemplate
29+
{
30+
Name = name,
31+
FileContent = ms.ToArray(),
32+
CreatedUtc = DateTime.UtcNow
33+
};
34+
var id = await _repo.InsertAsync(template);
35+
return Ok(new { id });
36+
}
37+
38+
[HttpGet("{id:guid}")]
39+
public async Task<IActionResult> Get(Guid id)
40+
{
41+
var t = await _repo.GetAsync(id);
42+
if (t == null)
43+
return NotFound();
44+
return File(t.FileContent, "application/vnd.openxmlformats-officedocument.wordprocessingml.document", t.Name + ".docx");
45+
}
46+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using eAuthor.Models;
2+
using eAuthor.Repositories;
3+
using eAuthor.Services.Background;
4+
using Microsoft.AspNetCore.Authorization;
5+
using Microsoft.AspNetCore.Mvc;
6+
using System.Text.Json;
7+
8+
namespace eAuthor.Controllers;
9+
10+
[ApiController]
11+
[Route("api/[controller]")]
12+
[Authorize]
13+
public class BatchGenerationController : ControllerBase {
14+
public record EnqueueBatchRequest(Guid TemplateId, JsonElement DataArray, string? BatchGroup);
15+
public record EnqueueBatchResponse(Guid CorrelationId, int Count);
16+
17+
private readonly IDocumentGenerationJobRepository _repo;
18+
private readonly IDocumentJobQueue _queue;
19+
20+
public BatchGenerationController(IDocumentGenerationJobRepository repo, IDocumentJobQueue queue) {
21+
_repo = repo;
22+
_queue = queue;
23+
}
24+
25+
[HttpPost("enqueue")]
26+
public async Task<IActionResult> Enqueue([FromBody] EnqueueBatchRequest req) {
27+
if (req.DataArray.ValueKind != JsonValueKind.Array)
28+
return BadRequest("DataArray must be JSON array of objects");
29+
var correlationId = Guid.NewGuid();
30+
var now = DateTime.UtcNow;
31+
var jobs = new List<DocumentGenerationJob>();
32+
var idx = 0;
33+
foreach (var item in req.DataArray.EnumerateArray()) {
34+
if (item.ValueKind != JsonValueKind.Object) continue;
35+
jobs.Add(new DocumentGenerationJob {
36+
Id = Guid.NewGuid(),
37+
TemplateId = req.TemplateId,
38+
Status = "Pending",
39+
InputData = item.GetRawText(),
40+
CreatedUtc = now.AddMilliseconds(idx++),
41+
CorrelationId = correlationId,
42+
BatchGroup = req.BatchGroup
43+
});
44+
}
45+
if (jobs.Count == 0) return BadRequest("No valid object items found");
46+
await _repo.InsertBatchAsync(jobs);
47+
_queue.SignalNewWork();
48+
return Ok(new EnqueueBatchResponse(correlationId, jobs.Count));
49+
}
50+
51+
[HttpGet("correlation/{id:guid}")]
52+
public async Task<IActionResult> GetByCorrelation(Guid id) {
53+
var jobs = await _repo.GetByCorrelationAsync(id);
54+
return Ok(jobs);
55+
}
56+
57+
[HttpGet("{id:guid}")]
58+
public async Task<IActionResult> Get(Guid id) {
59+
var job = await _repo.GetAsync(id);
60+
if (job == null) return NotFound();
61+
return Ok(job);
62+
}
63+
64+
[HttpGet("{id:guid}/result")]
65+
public async Task<IActionResult> Download(Guid id) {
66+
var job = await _repo.GetAsync(id);
67+
if (job == null) return NotFound();
68+
if (job.Status != "Completed" || job.ResultFile == null) return BadRequest("Not completed");
69+
return File(job.ResultFile, "application/vnd.openxmlformats-officedocument.wordprocessingml.document", $"{job.Id}.docx");
70+
}
71+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using eAuthor.Models;
2+
using eAuthor.Repositories;
3+
using eAuthor.Services;
4+
using Microsoft.AspNetCore.Authorization;
5+
using Microsoft.AspNetCore.Mvc;
6+
using System.Text.Json;
7+
8+
namespace eAuthor.Controllers;
9+
10+
[ApiController]
11+
[Route("api/[controller]")]
12+
[Authorize]
13+
public class GenerateController : ControllerBase
14+
{
15+
private readonly TemplateService _templateService;
16+
private readonly DocumentGenerationService _docService;
17+
private readonly IBaseDocxTemplateRepository _baseRepo;
18+
19+
public GenerateController(TemplateService t, DocumentGenerationService d, IBaseDocxTemplateRepository baseRepo)
20+
{
21+
_templateService = t;
22+
_docService = d;
23+
_baseRepo = baseRepo;
24+
}
25+
26+
public record GenerateRequest(Guid TemplateId, JsonElement Data);
27+
28+
[HttpPost]
29+
public async Task<IActionResult> Generate([FromBody] GenerateRequest req)
30+
{
31+
var template = await _templateService.GetAsync(req.TemplateId);
32+
if (template == null)
33+
return NotFound("Template not found");
34+
BaseDocxTemplate? baseDoc = null;
35+
if (template.GetType().GetProperty("BaseDocxTemplateId")?.GetValue(template) is Guid baseId && baseId != Guid.Empty)
36+
baseDoc = await _baseRepo.GetAsync(baseId);
37+
var bytes = _docService.Generate(template, req.Data, baseDoc);
38+
return File(bytes, "application/vnd.openxmlformats-officedocument.wordprocessingml.document", $"{template.Name}.docx");
39+
}
40+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using Microsoft.AspNetCore.Authorization;
2+
using Microsoft.AspNetCore.Mvc;
3+
using eAuthor.Repositories;
4+
using eAuthor.Services;
5+
using eAuthor.Models;
6+
7+
namespace eAuthor.Controllers;
8+
9+
[ApiController]
10+
[Route("api/templates")]
11+
[Authorize]
12+
public class TemplateConversionController : ControllerBase
13+
{
14+
private readonly TemplateService _templateService;
15+
private readonly HtmlToDynamicConverter _converter;
16+
private readonly DynamicDocxBuilderService _builder;
17+
private readonly IBaseDocxTemplateRepository _baseRepo;
18+
private readonly ITemplateRepository _templateRepo;
19+
20+
public TemplateConversionController(
21+
TemplateService templateService,
22+
HtmlToDynamicConverter converter,
23+
DynamicDocxBuilderService builder,
24+
IBaseDocxTemplateRepository baseRepo,
25+
ITemplateRepository templateRepo)
26+
{
27+
_templateService = templateService;
28+
_converter = converter;
29+
_builder = builder;
30+
_baseRepo = baseRepo;
31+
_templateRepo = templateRepo;
32+
}
33+
34+
public record ConvertRequest(bool attachAsBase);
35+
36+
[HttpPost("{id:guid}/convert-html-to-dynamic")]
37+
public async Task<IActionResult> Convert(Guid id, [FromBody] ConvertRequest req)
38+
{
39+
var template = await _templateService.GetAsync(id);
40+
if (template == null)
41+
return NotFound();
42+
43+
// Convert tokens to controls (additive – keep existing)
44+
var newControls = _converter.Convert(template.HtmlBody);
45+
// Merge (avoid duplicates by DataPath)
46+
var existingPaths = new HashSet<string>(template.Controls.Select(c => c.DataPath), StringComparer.OrdinalIgnoreCase);
47+
foreach (var c in newControls)
48+
if (!existingPaths.Contains(c.DataPath))
49+
{
50+
c.TemplateId = template.Id;
51+
template.Controls.Add(c);
52+
}
53+
await _templateRepo.UpsertAsync(template);
54+
55+
if (req.attachAsBase)
56+
{
57+
var bytes = _builder.Build(template);
58+
var baseDoc = new BaseDocxTemplate
59+
{
60+
Id = Guid.NewGuid(),
61+
Name = $"{template.Name}-dynamic",
62+
FileContent = bytes,
63+
CreatedUtc = DateTime.UtcNow
64+
};
65+
await _baseRepo.InsertAsync(baseDoc);
66+
// Attach by setting property via reflection (if model extended directly, set normally)
67+
var prop = template.GetType().GetProperty("BaseDocxTemplateId");
68+
if (prop != null)
69+
{
70+
prop.SetValue(template, baseDoc.Id);
71+
await _templateRepo.UpsertAsync(template);
72+
}
73+
return Ok(new { addedControls = newControls.Count, baseDocId = baseDoc.Id });
74+
}
75+
76+
return Ok(new { addedControls = newControls.Count });
77+
}
78+
}

0 commit comments

Comments
 (0)