2using Dreamine.Identity;
200 public IReadOnlyDictionary<string, TenantMediaUsageSummary>
MediaUsage {
get;
private set; }
201 =
new Dictionary<string, TenantMediaUsageSummary>(StringComparer.OrdinalIgnoreCase);
219 public IReadOnlyDictionary<string, MediaMigrationTenantStatus>
MigrationStatuses {
get;
private set; }
220 =
new Dictionary<string, MediaMigrationTenantStatus>(StringComparer.OrdinalIgnoreCase);
248 var settings = await
_globalSettings.GetAsync(ct).ConfigureAwait(
false);
249 settings.Normalize();
291 .ConfigureAwait(
false);
369 public IReadOnlyList<TenantConfig>
Tenants {
get;
private set; } = [];
450 public async Task
LoadAsync(CancellationToken ct =
default)
519 foreach (var tenant
in Tenants)
543 var result =
new Dictionary<string, TenantMediaUsageSummary>(StringComparer.OrdinalIgnoreCase);
544 foreach (var tenant
in Tenants)
546 if (
MediaUsage.TryGetValue(tenant.Slug, out var existing))
548 existing.ImageCount = tenant.GalleryFileNames.Count;
549 existing.VideoCount = tenant.VideoFileNames.Count;
551 result[tenant.Slug] = existing;
558 ImageCount = tenant.GalleryFileNames.Count,
559 VideoCount = tenant.VideoFileNames.Count,
561 LastModified = tenant.CreatedAt
600 return tenant.
GalleryFileNames.All(x => Path.GetExtension(x).Equals(
".webp", StringComparison.OrdinalIgnoreCase))
631 var slug =
NewSlug.Trim().ToLowerInvariant();
633 if (
string.IsNullOrWhiteSpace(slug))
646 {
StatusMessage =
"'admin'은 슬러그로 사용할 수 없습니다.";
return false; }
648 if (await
_tenants.ExistsAsync(slug, ct).ConfigureAwait(
false))
651 var user = await
_userContext.GetCurrentAsync().ConfigureAwait(
false);
657 PasswordHash = DreaminePasswordHasher.HashPassword(
NewPassword),
662 if (user.IsAuthenticated)
664 config.OwnerUserId = user.Id;
665 config.OwnerProvider = user.Provider;
666 config.OwnerEmail = user.Email;
667 config.OwnerDisplayName = user.DisplayName;
668 config.OwnerLinkedAt = DateTime.Now;
672 Provider = user.Provider,
674 DisplayName = user.DisplayName,
676 AddedAt = DateTime.Now
680 await
_tenants.SaveAsync(config, ct).ConfigureAwait(
false);
681 await
LoadAsync(ct).ConfigureAwait(
false);
740 public async Task
SetDisplayAsync(
string slug,
bool showOnHome,
int pinOrder, CancellationToken ct =
default)
742 var config = await
_tenants.GetAsync(slug, ct).ConfigureAwait(
false);
743 if (config is
null)
return;
744 config.ShowOnHome = showOnHome;
745 config.PinOrder = pinOrder;
746 await
_tenants.SaveAsync(config, ct).ConfigureAwait(
false);
747 await
LoadAsync(ct).ConfigureAwait(
false);
787 await
_tenants.DeleteAsync(slug, ct).ConfigureAwait(
false);
788 await
LoadAsync(ct).ConfigureAwait(
false);
839 var config = await
_tenants.GetAsync(slug, ct).ConfigureAwait(
false);
840 if (config is
null)
return;
843 config.MaxVideoSizeMb =
null;
844 await
_tenants.SaveAsync(config, ct).ConfigureAwait(
false);
845 await
LoadAsync(ct).ConfigureAwait(
false);
891 var config = await
_tenants.GetAsync(slug, ct).ConfigureAwait(
false);
892 if (config is
null)
return;
895 config.MaxVideoCount =
null;
896 await
_tenants.SaveAsync(config, ct).ConfigureAwait(
false);
897 await
LoadAsync(ct).ConfigureAwait(
false);
935 var config = await
_tenants.GetAsync(tenant.
Slug, ct).ConfigureAwait(
false);
936 if (config is
null)
return;
939 config.MaxVideoSizeMb =
null;
940 config.MaxVideoCount =
null;
942 await
_tenants.SaveAsync(config, ct).ConfigureAwait(
false);
943 await
LoadAsync(ct).ConfigureAwait(
false);
981 var config = await
_tenants.GetAsync(slug, ct).ConfigureAwait(
false);
982 if (config is
null)
return;
984 config.MediaPolicyOverride =
null;
985 config.MaxVideoSizeMb =
null;
986 config.MaxVideoCount =
null;
988 await
_tenants.SaveAsync(config, ct).ConfigureAwait(
false);
989 await
LoadAsync(ct).ConfigureAwait(
false);
1027 await
_mediaMigration.QueueTenantAsync(slug, ct).ConfigureAwait(
false);
1028 await
LoadAsync(ct).ConfigureAwait(
false);
1066 await
_mediaMigration.RetryTenantAsync(slug, ct).ConfigureAwait(
false);
1067 await
LoadAsync(ct).ConfigureAwait(
false);
1113 var config = await
_tenants.GetAsync(slug, ct).ConfigureAwait(
false);
1114 if (config is
null)
return;
1117 await
_tenants.SaveAsync(config, ct).ConfigureAwait(
false);
1118 await
LoadAsync(ct).ConfigureAwait(
false);
1164 var config = await
_tenants.GetAsync(slug, ct).ConfigureAwait(
false);
1165 if (config is
null)
return;
1168 await
_tenants.SaveAsync(config, ct).ConfigureAwait(
false);
1169 await
LoadAsync(ct).ConfigureAwait(
false);
1199 .Select(WeddingLayoutCatalog.FromLegacyKey)
1200 .Select(mode => WeddingLayoutCatalog.Instance.Find(mode))
1201 .Where(option => option is { Tier: WeddingLayoutTier.Premium })
1202 .Select(option => option!.Key)
1203 .Distinct(StringComparer.OrdinalIgnoreCase)
1233 .Select(WeddingThemeCatalog.NormalizeKey)
1234 .Select(key => WeddingThemeCatalog.Instance.Find(key))
1235 .Where(option => option is { Tier: WeddingThemeTier.Premium })
1236 .Select(option => option!.Key)
1237 .Distinct(StringComparer.OrdinalIgnoreCase)
1266 value.HasValue ? Math.Max(0, value.Value) :
null;
1294 if (value is
null || value.
IsEmpty)
1303 value.ImageQuality = value.ImageQuality.HasValue ? Math.Clamp(value.
ImageQuality.Value, 1, 100) :
null;
1308 return value.IsEmpty ? null : value;
static void Normalize(TenantConfig config)
static MediaPolicySettings CreateDefault()
string? ImageOutputFormat
int? ImageOptimizedMaxStorageMb
int? ImageOriginalMaxStorageMb
MediaPolicyOverride? MediaPolicyOverride
List< string > GalleryFileNames
readonly IMediaUsageQueryService _mediaUsage
async Task SetMaxVideoCountAsync(string slug, int? maxVideoCount, CancellationToken ct=default)
static ? MediaPolicyOverride NormalizeOverride(MediaPolicyOverride? value)
IReadOnlyList< TenantConfig > Tenants
async Task SaveGlobalSettingsAsync(CancellationToken ct=default)
Task< bool > LoginAsync()
async Task RefreshMediaUsageAsync(CancellationToken ct=default)
bool IsMediaUsageAccurate
static ? int NormalizeNullableNonNegative(int? value)
IReadOnlyDictionary< string, MediaMigrationTenantStatus > MigrationStatuses
async Task ReloadTenantsAsync(CancellationToken ct)
async Task SetMaxVideoSizeAsync(string slug, int? maxVideoSizeMb, CancellationToken ct=default)
readonly WeddingUserContext _userContext
async Task< bool > CreateTenantAsync(CancellationToken ct=default)
readonly ISuperAdminSessionTokenService _superAdminTokens
async Task LoadGlobalSettingsAsync(CancellationToken ct=default)
async Task SaveMediaOverrideAsync(TenantConfig tenant, CancellationToken ct=default)
WeddingSuperAdminViewModel(ITenantStore tenants, WeddingOptions opts, IGlobalSettingsStore globalSettings, WeddingUserContext userContext, IMediaUsageQueryService mediaUsage, IMediaMigrationService mediaMigration, ISuperAdminSessionTokenService superAdminTokens)
async Task QueueImageMigrationAsync(string slug, CancellationToken ct=default)
static List< string > NormalizeUnlockedLayoutKeys(IEnumerable< string > unlockedLayoutModes)
readonly IMediaMigrationService _mediaMigration
string SuperAdminSessionToken
async Task ResetMediaOverrideAsync(string slug, CancellationToken ct=default)
async Task SetUnlockedLayoutsAsync(string slug, IEnumerable< string > unlockedLayoutModes, CancellationToken ct=default)
MediaPolicySettings MediaPolicy
IReadOnlyDictionary< string, TenantMediaUsageSummary > BuildFastMediaUsage()
IReadOnlyDictionary< string, TenantMediaUsageSummary > MediaUsage
static List< string > NormalizeUnlockedThemeKeys(IEnumerable< string > unlockedThemeKeys)
async Task RetryImageMigrationAsync(string slug, CancellationToken ct=default)
async Task LoadAsync(CancellationToken ct=default)
async Task SetDisplayAsync(string slug, bool showOnHome, int pinOrder, CancellationToken ct=default)
async Task DeleteTenantAsync(string slug, CancellationToken ct=default)
async Task SetUnlockedThemesAsync(string slug, IEnumerable< string > unlockedThemeKeys, CancellationToken ct=default)
readonly WeddingOptions _opts
readonly IGlobalSettingsStore _globalSettings
MediaMigrationState ResolveMigrationState(TenantConfig tenant)
readonly ITenantStore _tenants