Codemaru 1.0.0.0
QR 코드, 모바일 랜딩 페이지, vCard 연락처 저장과 명함 디자인을 한 화면에서 제공하는 디지털 명함 서비스입니다.
로딩중...
검색중...
일치하는것 없음
JsonCardProfileStore.cs
이 파일의 문서화 페이지로 가기
2using System.IO;
3using System.Text.Json;
4
5namespace Codemaru.Services;
6
24{
33 private const string SnapshotFileName = "snapshot.json";
34
43 private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web)
44 {
45 WriteIndented = true
46 };
47
56 private readonly string _rootDirectory;
57
67 {
68 _rootDirectory = Path.Combine(AppContext.BaseDirectory, "App_Data", "Cards");
69 Directory.CreateDirectory(_rootDirectory);
70 }
71
104 public async Task<CardHybridSnapshot?> LoadAsync(string userId, CancellationToken cancellationToken = default)
105 {
106 if (IsGuest(userId))
107 {
108 return null;
109 }
110
111 var filePath = GetSnapshotPath(userId);
112 if (!File.Exists(filePath))
113 {
114 return null;
115 }
116
117 await using var stream = File.OpenRead(filePath);
118 var snapshot = await JsonSerializer.DeserializeAsync<CardHybridSnapshot>(
119 stream, SerializerOptions, cancellationToken).ConfigureAwait(false);
120
121 return snapshot?.UserId == userId ? snapshot : snapshot;
122 }
123
164 public async Task SaveAsync(string userId, CardHybridSnapshot snapshot, CancellationToken cancellationToken = default)
165 {
166 ArgumentNullException.ThrowIfNull(snapshot);
167
168 if (IsGuest(userId))
169 {
170 return;
171 }
172
173 var filePath = GetSnapshotPath(userId);
174 var directory = Path.GetDirectoryName(filePath)!;
175 Directory.CreateDirectory(directory);
176
177 var tempPath = filePath + ".tmp";
178 await using (var stream = File.Create(tempPath))
179 {
180 await JsonSerializer.SerializeAsync(stream, snapshot, SerializerOptions, cancellationToken)
181 .ConfigureAwait(false);
182 }
183
184 File.Copy(tempPath, filePath, overwrite: true);
185 File.Delete(tempPath);
186 }
187
220 public async Task<CardHybridSnapshot?> LoadBySlugAsync(string slug, CancellationToken cancellationToken = default)
221 {
222 if (string.IsNullOrWhiteSpace(slug))
223 {
224 return null;
225 }
226
227 var normalizedSlug = slug.Trim().Trim('/').ToLowerInvariant();
228
229 foreach (var userDirectory in Directory.EnumerateDirectories(_rootDirectory))
230 {
231 cancellationToken.ThrowIfCancellationRequested();
232
233 var filePath = Path.Combine(userDirectory, SnapshotFileName);
234 if (!File.Exists(filePath))
235 {
236 continue;
237 }
238
239 try
240 {
241 await using var stream = File.OpenRead(filePath);
242 var snapshot = await JsonSerializer.DeserializeAsync<CardHybridSnapshot>(
243 stream, SerializerOptions, cancellationToken).ConfigureAwait(false);
244
245 if (snapshot?.Profile is null)
246 {
247 continue;
248 }
249
250 var profileSlug = snapshot.Profile.LandingSlug.Trim().Trim('/').ToLowerInvariant();
251 if (profileSlug == $"card/{normalizedSlug}" || profileSlug == normalizedSlug)
252 {
253 return snapshot;
254 }
255 }
256 catch
257 {
258 // skip corrupt files
259 }
260 }
261
262 return null;
263 }
264
297 public Task DeleteAsync(string userId, CancellationToken cancellationToken = default)
298 {
299 if (IsGuest(userId))
300 {
301 return Task.CompletedTask;
302 }
303
304 var filePath = GetSnapshotPath(userId);
305 if (File.Exists(filePath))
306 {
307 File.Delete(filePath);
308 }
309
310 return Task.CompletedTask;
311 }
312
337 private string GetSnapshotPath(string userId)
338 {
339 return Path.Combine(_rootDirectory, SanitizeUserId(userId), SnapshotFileName);
340 }
341
366 private static bool IsGuest(string userId) =>
367 string.IsNullOrWhiteSpace(userId) ||
368 string.Equals(userId, CardHybridUser.Guest.Id, StringComparison.OrdinalIgnoreCase);
369
394 private static string SanitizeUserId(string userId)
395 {
396 var safe = new string((string.IsNullOrWhiteSpace(userId) ? "guest" : userId)
397 .Select(static c => char.IsLetterOrDigit(c) || c is '-' or '_' ? c : '_')
398 .ToArray());
399 return string.IsNullOrWhiteSpace(safe) ? "guest" : safe;
400 }
401}
record CardHybridSnapshot(string? UserId, CardProfile Profile, IReadOnlyList< CardHistoryEntry > History, DateTime SavedAt)
record CardHybridUser(string Id, string Email, string DisplayName, DateTime SignedInAt)
async Task< CardHybridSnapshot?> LoadBySlugAsync(string slug, CancellationToken cancellationToken=default)
async Task< CardHybridSnapshot?> LoadAsync(string userId, CancellationToken cancellationToken=default)
static string SanitizeUserId(string userId)
Task DeleteAsync(string userId, CancellationToken cancellationToken=default)
static readonly JsonSerializerOptions SerializerOptions
async Task SaveAsync(string userId, CardHybridSnapshot snapshot, CancellationToken cancellationToken=default)