Skip to content

Commit 0d01a8e

Browse files
authored
Merge pull request #13 from csharpfritz/fix-ContentSyncLog
2 parents 800256f + 0633733 commit 0d01a8e

4 files changed

Lines changed: 115 additions & 58 deletions

File tree

ContentLoader/Program.cs

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,25 +43,49 @@
4343
{
4444
Console.WriteLine($"No markdown files found in directory: {filePath}");
4545
return 1;
46-
}
47-
int successCount = 0;
48-
int failCount = 0;
46+
} int addedCount = 0;
47+
int updatedCount = 0;
48+
int unchangedCount = 0;
49+
int failedCount = 0;
50+
4951
foreach (var mdFile in mdFiles)
5052
{
5153
try
5254
{
5355
TipModel tip = Shared.ContentUploadHelper.ParseMarkdownFile(mdFile);
54-
await Shared.ContentUploadHelper.UploadToTableStorage(tip, connectionString);
55-
successCount++;
56+
var status = await Shared.ContentUploadHelper.UploadToTableStorage(tip, connectionString);
57+
58+
switch (status)
59+
{
60+
case UploadStatus.Added:
61+
addedCount++;
62+
break;
63+
case UploadStatus.Updated:
64+
updatedCount++;
65+
break;
66+
case UploadStatus.Unchanged:
67+
unchangedCount++;
68+
break;
69+
case UploadStatus.Failed:
70+
failedCount++;
71+
break;
72+
}
5673
}
5774
catch (Exception ex)
5875
{
5976
Console.WriteLine($"Error uploading {mdFile}: {ex.Message}");
60-
failCount++;
77+
failedCount++;
6178
}
6279
}
63-
Console.WriteLine($"Upload complete. Success: {successCount}, Failed: {failCount}");
64-
return failCount == 0 ? 0 : 1;
80+
81+
Console.WriteLine("\nSync Summary:");
82+
Console.WriteLine($"Added: {addedCount}");
83+
Console.WriteLine($"Updated: {updatedCount}");
84+
Console.WriteLine($"Unchanged: {unchangedCount}");
85+
Console.WriteLine($"Failed: {failedCount}");
86+
Console.WriteLine($"Total: {mdFiles.Length}");
87+
88+
return failedCount == 0 ? 0 : 1;
6589
}
6690
else
6791
{

Shared/ContentUploadHelper.cs

Lines changed: 53 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -115,56 +115,64 @@ private static string CalculateContentHash(TipModel tip)
115115
// Entity doesn't exist or other error - treat as not found
116116
return null;
117117
}
118-
}
119-
120-
public static async Task UploadToTableStorage(TipModel tip, string connectionString)
118+
} public static async Task<UploadStatus> UploadToTableStorage(TipModel tip, string connectionString)
121119
{
122-
var serviceClient = new TableServiceClient(connectionString);
123-
var tableName = "Content";
124-
await serviceClient.CreateTableIfNotExistsAsync(tableName);
125-
var tableClient = serviceClient.GetTableClient(tableName);
126-
127-
var partitionKey = tip.Category.ToLowerInvariant();
128-
var rowKey = !string.IsNullOrWhiteSpace(tip.UrlSlug) ? tip.UrlSlug : tip.FileName;
129-
130-
// Calculate content hash for change detection
131-
var contentHash = CalculateContentHash(tip);
132-
133-
// Check if entity already exists
134-
var existingEntity = await GetExistingContent(tableClient, partitionKey, rowKey);
135-
136-
if (existingEntity != null && existingEntity.ContentHash == contentHash)
120+
try
137121
{
138-
Console.WriteLine($"Skipping {tip.FileName} - no changes detected");
139-
return;
140-
}
122+
var serviceClient = new TableServiceClient(connectionString);
123+
var tableName = "Content";
124+
await serviceClient.CreateTableIfNotExistsAsync(tableName);
125+
var tableClient = serviceClient.GetTableClient(tableName);
141126

142-
var entity = new ContentEntity
143-
{
144-
PartitionKey = partitionKey,
145-
RowKey = rowKey,
146-
Slug = tip.UrlSlug,
147-
Title = tip.Title,
148-
Category = tip.Category,
149-
Tags = string.Join(",", tip.Tags),
150-
Difficulty = tip.Difficulty,
151-
Author = tip.Author,
152-
PublishedDate = DateTime.SpecifyKind(tip.PublishedDate, DateTimeKind.Utc),
153-
Description = tip.Description,
154-
Content = tip.Content,
155-
FileName = tip.FileName,
156-
ContentHash = contentHash
157-
};
158-
159-
await tableClient.UpsertEntityAsync(entity);
160-
161-
if (existingEntity == null)
162-
{
163-
Console.WriteLine($"Uploaded new content: {tip.FileName}");
127+
var partitionKey = tip.Category.ToLowerInvariant();
128+
var rowKey = !string.IsNullOrWhiteSpace(tip.UrlSlug) ? tip.UrlSlug : tip.FileName;
129+
130+
// Calculate content hash for change detection
131+
var contentHash = CalculateContentHash(tip);
132+
133+
// Check if entity already exists
134+
var existingEntity = await GetExistingContent(tableClient, partitionKey, rowKey);
135+
136+
if (existingEntity != null && existingEntity.ContentHash == contentHash)
137+
{
138+
Console.WriteLine($"Skipping {tip.FileName} - no changes detected");
139+
return UploadStatus.Unchanged;
140+
}
141+
142+
var entity = new ContentEntity
143+
{
144+
PartitionKey = partitionKey,
145+
RowKey = rowKey,
146+
Slug = tip.UrlSlug,
147+
Title = tip.Title,
148+
Category = tip.Category,
149+
Tags = string.Join(",", tip.Tags),
150+
Difficulty = tip.Difficulty,
151+
Author = tip.Author,
152+
PublishedDate = DateTime.SpecifyKind(tip.PublishedDate, DateTimeKind.Utc),
153+
Description = tip.Description,
154+
Content = tip.Content,
155+
FileName = tip.FileName,
156+
ContentHash = contentHash
157+
};
158+
159+
await tableClient.UpsertEntityAsync(entity);
160+
161+
if (existingEntity == null)
162+
{
163+
Console.WriteLine($"Uploaded new content: {tip.FileName}");
164+
return UploadStatus.Added;
165+
}
166+
else
167+
{
168+
Console.WriteLine($"Updated changed content: {tip.FileName}");
169+
return UploadStatus.Updated;
170+
}
164171
}
165-
else
172+
catch (Exception ex)
166173
{
167-
Console.WriteLine($"Updated changed content: {tip.FileName}");
174+
Console.WriteLine($"Failed to upload {tip.FileName}: {ex.Message}");
175+
return UploadStatus.Failed;
168176
}
169177
}
170178
}

Shared/UploadStatus.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace Shared;
2+
3+
public enum UploadStatus
4+
{
5+
Added,
6+
Updated,
7+
Unchanged,
8+
Failed
9+
}

Web/Extensions/EndpointExtensions.cs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Xml.Linq;
44
using Microsoft.AspNetCore.Builder;
55
using Microsoft.AspNetCore.Http;
6+
using Microsoft.AspNetCore.OutputCaching;
67
using Web.Services;
78

89
namespace Web.Extensions;
@@ -195,7 +196,7 @@ public static class EndpointExtensions
195196

196197
public static void MapCacheRefreshEndpoint(this WebApplication app)
197198
{
198-
app.MapPost("/api/cache/refresh", async (HttpContext context, IContentService contentService, ILogger<Program> logger, IWebHostEnvironment environment, IConfiguration configuration) =>
199+
app.MapPost("/api/cache/refresh", async (HttpContext context, IContentService contentService, IOutputCacheStore outputCacheStore, ILogger<Program> logger, IWebHostEnvironment environment, IConfiguration configuration) =>
199200
{
200201
try
201202
{
@@ -219,7 +220,6 @@ public static void MapCacheRefreshEndpoint(this WebApplication app)
219220
}
220221

221222
// Only perform cache refresh in non-development environments
222-
// In development, the cache isn't as critical and may require Azure Table Storage
223223
if (environment.IsDevelopment())
224224
{
225225
logger.LogInformation("Development environment detected. Skipping cache refresh.");
@@ -230,10 +230,26 @@ public static void MapCacheRefreshEndpoint(this WebApplication app)
230230
});
231231
}
232232

233+
// Refresh content cache
233234
await contentService.RefreshContentAsync();
234235

235-
logger.LogInformation("Cache refresh completed successfully");
236-
return Results.Ok(new { message = "Cache refreshed successfully", timestamp = DateTime.UtcNow });
236+
// Evict all output cache entries
237+
await outputCacheStore.EvictByTagAsync("sitemap", default);
238+
await outputCacheStore.EvictByTagAsync("rss", default);
239+
await outputCacheStore.EvictByTagAsync("Web.Pages.IndexModel", default);
240+
await outputCacheStore.EvictByTagAsync("Web.Pages.Tips.IndexModel", default);
241+
await outputCacheStore.EvictByTagAsync("Web.Pages.Tips.CategoryModel", default);
242+
await outputCacheStore.EvictByTagAsync("Web.Pages.Tips.TagModel", default);
243+
await outputCacheStore.EvictByTagAsync("Web.Pages.Tips.DetailsModel", default);
244+
245+
// Also evict by the base policy
246+
await outputCacheStore.EvictByTagAsync("", default);
247+
248+
logger.LogInformation("Cache refresh completed successfully (content and output cache)");
249+
return Results.Ok(new {
250+
message = "Content and output cache refreshed successfully",
251+
timestamp = DateTime.UtcNow
252+
});
237253
}
238254
catch (Exception ex)
239255
{
@@ -243,6 +259,6 @@ public static void MapCacheRefreshEndpoint(this WebApplication app)
243259
})
244260
.WithName("RefreshCache")
245261
.WithSummary("Refresh the content cache")
246-
.WithDescription("Triggers a refresh of the in-memory content cache from Azure Table Storage. Requires X-API-Key header for authentication.");
262+
.WithDescription("Triggers a refresh of the in-memory content cache and output cache for all pages. Requires X-API-Key header for authentication.");
247263
}
248264
}

0 commit comments

Comments
 (0)