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))
645 if (slug is
"admin" or
"superadmin")
646 {
StatusMessage =
"'admin/superadmin'은 슬러그로 사용할 수 없습니다.";
return false; }
648 if (await
_tenants.ExistsAsync(slug, ct).ConfigureAwait(
false))
651 var user = await
_userContext.GetCurrentAsync().ConfigureAwait(
false);
657 PasswordHash = DreaminePasswordHasher.HashPassword(
NewPassword),
661 if (user.IsAuthenticated)
663 config.OwnerUserId = user.Id;
664 config.OwnerProvider = user.Provider;
665 config.OwnerEmail = user.Email;
666 config.OwnerDisplayName = user.DisplayName;
667 config.OwnerLinkedAt = DateTime.Now;
671 Provider = user.Provider,
673 DisplayName = user.DisplayName,
675 AddedAt = DateTime.Now
679 await
_tenants.SaveAsync(config, ct).ConfigureAwait(
false);
680 await
LoadAsync(ct).ConfigureAwait(
false);
739 public async Task
SetDisplayAsync(
string slug,
bool showOnHome,
int pinOrder, CancellationToken ct =
default)
741 var config = await
_tenants.GetAsync(slug, ct).ConfigureAwait(
false);
742 if (config is
null)
return;
743 config.ShowOnHome = showOnHome;
744 config.PinOrder = pinOrder;
745 await
_tenants.SaveAsync(config, ct).ConfigureAwait(
false);
746 await
LoadAsync(ct).ConfigureAwait(
false);
786 await
_tenants.DeleteAsync(slug, ct).ConfigureAwait(
false);
787 await
LoadAsync(ct).ConfigureAwait(
false);
838 var config = await
_tenants.GetAsync(slug, ct).ConfigureAwait(
false);
839 if (config is
null)
return;
842 config.MaxVideoSizeMb =
null;
843 await
_tenants.SaveAsync(config, ct).ConfigureAwait(
false);
844 await
LoadAsync(ct).ConfigureAwait(
false);
890 var config = await
_tenants.GetAsync(slug, ct).ConfigureAwait(
false);
891 if (config is
null)
return;
894 config.MaxVideoCount =
null;
895 await
_tenants.SaveAsync(config, ct).ConfigureAwait(
false);
896 await
LoadAsync(ct).ConfigureAwait(
false);
934 var config = await
_tenants.GetAsync(tenant.
Slug, ct).ConfigureAwait(
false);
935 if (config is
null)
return;
938 config.MaxVideoSizeMb =
null;
939 config.MaxVideoCount =
null;
941 await
_tenants.SaveAsync(config, ct).ConfigureAwait(
false);
942 await
LoadAsync(ct).ConfigureAwait(
false);
980 var config = await
_tenants.GetAsync(slug, ct).ConfigureAwait(
false);
981 if (config is
null)
return;
983 config.MediaPolicyOverride =
null;
984 config.MaxVideoSizeMb =
null;
985 config.MaxVideoCount =
null;
987 await
_tenants.SaveAsync(config, ct).ConfigureAwait(
false);
988 await
LoadAsync(ct).ConfigureAwait(
false);
1026 await
_mediaMigration.QueueTenantAsync(slug, ct).ConfigureAwait(
false);
1027 await
LoadAsync(ct).ConfigureAwait(
false);
1065 await
_mediaMigration.RetryTenantAsync(slug, ct).ConfigureAwait(
false);
1066 await
LoadAsync(ct).ConfigureAwait(
false);
1112 var config = await
_tenants.GetAsync(slug, ct).ConfigureAwait(
false);
1113 if (config is
null)
return;
1116 await
_tenants.SaveAsync(config, ct).ConfigureAwait(
false);
1117 await
LoadAsync(ct).ConfigureAwait(
false);
1163 var config = await
_tenants.GetAsync(slug, ct).ConfigureAwait(
false);
1164 if (config is
null)
return;
1167 await
_tenants.SaveAsync(config, ct).ConfigureAwait(
false);
1168 await
LoadAsync(ct).ConfigureAwait(
false);
1198 .Select(WeddingLayoutCatalog.FromLegacyKey)
1199 .Select(mode => WeddingLayoutCatalog.Instance.Find(mode))
1200 .Where(option => option is { Tier: WeddingLayoutTier.Premium })
1201 .Select(option => option!.Key)
1202 .Distinct(StringComparer.OrdinalIgnoreCase)
1232 .Select(WeddingThemeCatalog.NormalizeKey)
1233 .Select(key => WeddingThemeCatalog.Instance.Find(key))
1234 .Where(option => option is { Tier: WeddingThemeTier.Premium })
1235 .Select(option => option!.Key)
1236 .Distinct(StringComparer.OrdinalIgnoreCase)
1265 value.HasValue ? Math.Max(0, value.Value) :
null;
1293 if (value is
null || value.
IsEmpty)
1302 value.ImageQuality = value.ImageQuality.HasValue ? Math.Clamp(value.
ImageQuality.Value, 1, 100) :
null;
1307 return value.IsEmpty ? null : value;
static MediaPolicySettings CreateDefault()
string? ImageOutputFormat
int? ImageOptimizedMaxStorageMb
int? ImageOriginalMaxStorageMb
List< string > GalleryFileNames
MediaPolicyOverride? MediaPolicyOverride
static void Normalize(TenantConfig config)
async Task QueueImageMigrationAsync(string slug, CancellationToken ct=default)
string SuperAdminSessionToken
async Task DeleteTenantAsync(string slug, CancellationToken ct=default)
readonly WeddingOptions _opts
async Task SetMaxVideoSizeAsync(string slug, int? maxVideoSizeMb, CancellationToken ct=default)
async Task SetUnlockedLayoutsAsync(string slug, IEnumerable< string > unlockedLayoutModes, CancellationToken ct=default)
readonly IMediaMigrationService _mediaMigration
Task< bool > LoginAsync()
bool IsMediaUsageAccurate
static List< string > NormalizeUnlockedThemeKeys(IEnumerable< string > unlockedThemeKeys)
async Task SaveGlobalSettingsAsync(CancellationToken ct=default)
async Task LoadGlobalSettingsAsync(CancellationToken ct=default)
async Task SaveMediaOverrideAsync(TenantConfig tenant, CancellationToken ct=default)
readonly IGlobalSettingsStore _globalSettings
SuperAdminViewModel(ITenantStore tenants, WeddingOptions opts, IGlobalSettingsStore globalSettings, ThankYouUserContext userContext, IMediaUsageQueryService mediaUsage, IMediaMigrationService mediaMigration, ISuperAdminSessionTokenService superAdminTokens)
async Task SetUnlockedThemesAsync(string slug, IEnumerable< string > unlockedThemeKeys, CancellationToken ct=default)
static ? int NormalizeNullableNonNegative(int? value)
static List< string > NormalizeUnlockedLayoutKeys(IEnumerable< string > unlockedLayoutModes)
IReadOnlyDictionary< string, MediaMigrationTenantStatus > MigrationStatuses
async Task SetMaxVideoCountAsync(string slug, int? maxVideoCount, CancellationToken ct=default)
MediaPolicySettings MediaPolicy
MediaMigrationState ResolveMigrationState(TenantConfig tenant)
readonly ThankYouUserContext _userContext
readonly IMediaUsageQueryService _mediaUsage
IReadOnlyList< TenantConfig > Tenants
async Task ResetMediaOverrideAsync(string slug, CancellationToken ct=default)
readonly ITenantStore _tenants
async Task< bool > CreateTenantAsync(CancellationToken ct=default)
static ? MediaPolicyOverride NormalizeOverride(MediaPolicyOverride? value)
IReadOnlyDictionary< string, TenantMediaUsageSummary > MediaUsage
async Task ReloadTenantsAsync(CancellationToken ct)
async Task RefreshMediaUsageAsync(CancellationToken ct=default)
async Task LoadAsync(CancellationToken ct=default)
async Task RetryImageMigrationAsync(string slug, CancellationToken ct=default)
IReadOnlyDictionary< string, TenantMediaUsageSummary > BuildFastMediaUsage()
readonly ISuperAdminSessionTokenService _superAdminTokens
async Task SetDisplayAsync(string slug, bool showOnHome, int pinOrder, CancellationToken ct=default)