Families.Web 1.0.0.0
사진, 동영상, 글, 댓글과 반응을 가족끼리만 공유하는 비공개 앨범·타임라인 서비스입니다.
로딩중...
검색중...
일치하는것 없음
JsonAlbumStore.cs
이 파일의 문서화 페이지로 가기
1using System.IO;
2using System.Text.Json;
4
6
15public sealed class JsonAlbumStore : IAlbumStore
16{
25 private readonly IFamilyTenantStore _tenants;
34 private static readonly SemaphoreSlim _gate = new(1, 1);
35
44 private static readonly JsonSerializerOptions _jsonOpts = new()
45 {
46 WriteIndented = true,
47 PropertyNameCaseInsensitive = true
48 };
49
66 public JsonAlbumStore(IFamilyTenantStore tenants) => _tenants = tenants;
67
92 private string AlbumsDir(string slug) =>
93 Path.Combine(_tenants.GetTenantDataPath(slug), "albums");
94
127 private string AlbumPath(string slug, string albumId) =>
128 Path.Combine(AlbumsDir(slug), $"{Sanitize(albumId)}.json");
129
162 public async Task<IReadOnlyList<AlbumInfo>> GetAllAsync(string slug, CancellationToken ct = default)
163 {
164 var dir = AlbumsDir(slug);
165 if (!Directory.Exists(dir)) return [];
166
167 var list = new List<AlbumInfo>();
168 foreach (var file in Directory.GetFiles(dir, "*.json"))
169 {
170 await _gate.WaitAsync(ct).ConfigureAwait(false);
171 try
172 {
173 await using var fs = File.OpenRead(file);
174 var a = await JsonSerializer.DeserializeAsync<AlbumInfo>(fs, _jsonOpts, ct).ConfigureAwait(false);
175 if (a != null) list.Add(a);
176 }
177 finally { _gate.Release(); }
178 }
179 return list.OrderBy(a => a.SortOrder == 0 ? int.MaxValue : a.SortOrder)
180 .ThenBy(a => a.CreatedAt)
181 .ToList();
182 }
183
224 public async Task<AlbumInfo?> GetAsync(string slug, string albumId, CancellationToken ct = default)
225 {
226 var path = AlbumPath(slug, albumId);
227 if (!File.Exists(path)) return null;
228
229 await _gate.WaitAsync(ct).ConfigureAwait(false);
230 try
231 {
232 await using var fs = File.OpenRead(path);
233 return await JsonSerializer.DeserializeAsync<AlbumInfo>(fs, _jsonOpts, ct).ConfigureAwait(false);
234 }
235 finally { _gate.Release(); }
236 }
237
278 public async Task SaveAsync(string slug, AlbumInfo album, CancellationToken ct = default)
279 {
280 var dir = AlbumsDir(slug);
281 Directory.CreateDirectory(dir);
282
283 var path = AlbumPath(slug, album.Id);
284 var tmp = path + ".tmp";
285
286 await _gate.WaitAsync(ct).ConfigureAwait(false);
287 try
288 {
289 await using (var fs = File.Create(tmp))
290 await JsonSerializer.SerializeAsync(fs, album, _jsonOpts, ct).ConfigureAwait(false);
291
292 File.Copy(tmp, path, overwrite: true);
293 File.Delete(tmp);
294 }
295 finally { _gate.Release(); }
296 }
297
338 public Task DeleteAsync(string slug, string albumId, CancellationToken ct = default)
339 {
340 var path = AlbumPath(slug, albumId);
341 if (File.Exists(path)) File.Delete(path);
342 return Task.CompletedTask;
343 }
344
369 private static string Sanitize(string id) =>
370 string.Concat(id.Split(Path.GetInvalidFileNameChars()));
371}
static readonly JsonSerializerOptions _jsonOpts
async Task< IReadOnlyList< AlbumInfo > > GetAllAsync(string slug, CancellationToken ct=default)
static readonly SemaphoreSlim _gate
async Task< AlbumInfo?> GetAsync(string slug, string albumId, CancellationToken ct=default)
async Task SaveAsync(string slug, AlbumInfo album, CancellationToken ct=default)
string AlbumPath(string slug, string albumId)
static string Sanitize(string id)
JsonAlbumStore(IFamilyTenantStore tenants)
Task DeleteAsync(string slug, string albumId, CancellationToken ct=default)
readonly IFamilyTenantStore _tenants