using FlatRender.StudioSvc.Domain.Entities; using FlatRender.StudioSvc.Domain.Enums; using Microsoft.EntityFrameworkCore; namespace FlatRender.StudioSvc.Infrastructure.Data; public class StudioDbContext(DbContextOptions options) : DbContext(options) { public DbSet SavedProjects => Set(); public DbSet SavedScenes => Set(); public DbSet SavedSceneContents => Set(); public DbSet SavedSceneColors => Set(); public DbSet SavedSceneColorPresets => Set(); public DbSet SavedSceneColorPresetItems => Set(); public DbSet SavedSceneCharacters => Set(); public DbSet SavedSceneCharacterControllers => Set(); public DbSet SavedSharedColors => Set(); public DbSet SavedSharedColorPresets => Set(); public DbSet SavedSharedColorPresetItems => Set(); public DbSet SavedSharedLayers => Set(); protected override void OnModelCreating(ModelBuilder mb) { mb.HasDefaultSchema("studio"); // Native PostgreSQL enum registered on the EF provider via npgsql.MapEnum() // in Program.cs (EF Core 9+ approach), covering model + runtime ADO mapping. ConfigureSavedProjects(mb); ConfigureSavedScenes(mb); ConfigureSharedData(mb); } private static void ConfigureSavedProjects(ModelBuilder mb) { mb.Entity(e => { e.ToTable("saved_projects"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id"); e.Property(x => x.TenantId).HasColumnName("tenant_id"); e.Property(x => x.UserId).HasColumnName("user_id"); e.Property(x => x.OriginalProjectId).HasColumnName("original_project_id"); e.Property(x => x.OriginalProjectName).HasColumnName("original_project_name").IsRequired(); e.Property(x => x.OriginalContainerId).HasColumnName("original_container_id"); e.Property(x => x.OriginalContainerSlug).HasColumnName("original_container_slug").HasColumnType("citext"); e.Property(x => x.Name).HasColumnName("name").IsRequired(); e.Property(x => x.Image).HasColumnName("image"); e.Property(x => x.Type).HasColumnName("type"); e.Property(x => x.FrameRate).HasColumnName("frame_rate"); e.Property(x => x.ProjectDurationSec).HasColumnName("project_duration_sec"); e.Property(x => x.Resolution).HasColumnName("resolution").IsRequired(); e.Property(x => x.ChooseMode).HasColumnName("choose_mode").IsRequired(); e.Property(x => x.VipFactor).HasColumnName("vip_factor"); e.Property(x => x.MusicFileId).HasColumnName("music_file_id"); e.Property(x => x.MusicTrackId).HasColumnName("music_track_id"); e.Property(x => x.MusicVolume).HasColumnName("music_volume"); e.Property(x => x.VoiceoverFileId).HasColumnName("voiceover_file_id"); e.Property(x => x.VoiceoverVolume).HasColumnName("voiceover_volume"); e.Property(x => x.VoiceoverRecordedInBrowser).HasColumnName("voiceover_recorded_in_browser"); e.Property(x => x.SfxVolume).HasColumnName("sfx_volume"); e.Property(x => x.SfxEnabled).HasColumnName("sfx_enabled"); e.Property(x => x.AudioVisualizerMusicUrl).HasColumnName("audio_visualizer_music_url"); e.Property(x => x.AudioVisualizerDurationSec).HasColumnName("audio_visualizer_duration_sec"); e.Property(x => x.ManualColorPicker).HasColumnName("manual_color_picker"); e.Property(x => x.SelectedPresetStoryId).HasColumnName("selected_preset_story_id"); e.Property(x => x.LastEditStep).HasColumnName("last_edit_step"); e.Property(x => x.EditState).HasColumnName("edit_state").HasColumnType("jsonb").IsRequired(); e.Property(x => x.CreateDate).HasColumnName("create_date"); e.Property(x => x.LastEditDate).HasColumnName("last_edit_date"); e.Property(x => x.CreatedAt).HasColumnName("created_at"); e.Property(x => x.UpdatedAt).HasColumnName("updated_at"); e.Property(x => x.DeletedAt).HasColumnName("deleted_at"); e.HasQueryFilter(x => x.DeletedAt == null); }); } private static void ConfigureSavedScenes(ModelBuilder mb) { mb.Entity(e => { e.ToTable("saved_scenes"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id").UseIdentityByDefaultColumn(); e.Property(x => x.SavedProjectId).HasColumnName("saved_project_id"); e.Property(x => x.OriginalSceneId).HasColumnName("original_scene_id"); e.Property(x => x.Key).HasColumnName("key").IsRequired(); e.Property(x => x.Title).HasColumnName("title"); e.Property(x => x.Image).HasColumnName("image"); e.Property(x => x.Demo).HasColumnName("demo"); e.Property(x => x.SceneColorSvg).HasColumnName("scene_color_svg"); e.Property(x => x.SceneType).HasColumnName("scene_type").IsRequired(); e.Property(x => x.Sort).HasColumnName("sort"); e.Property(x => x.SceneLengthSec).HasColumnName("scene_length_sec"); e.Property(x => x.MinDurationSec).HasColumnName("min_duration_sec"); e.Property(x => x.MaxDurationSec).HasColumnName("max_duration_sec"); e.Property(x => x.OverlapAtEndSec).HasColumnName("overlap_at_end_sec"); e.Property(x => x.CanHandleDuration).HasColumnName("can_handle_duration"); e.Property(x => x.ManualColorSelection).HasColumnName("manual_color_selection"); e.Property(x => x.SelectedColorPresetId).HasColumnName("selected_color_preset_id"); e.Property(x => x.CreatedAt).HasColumnName("created_at"); e.Property(x => x.UpdatedAt).HasColumnName("updated_at"); e.HasOne(x => x.SavedProject).WithMany(x => x.Scenes).HasForeignKey(x => x.SavedProjectId).OnDelete(DeleteBehavior.Cascade); }); mb.Entity(e => { e.ToTable("saved_scene_contents"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id").UseIdentityByDefaultColumn(); e.Property(x => x.SavedSceneId).HasColumnName("saved_scene_id"); e.Property(x => x.Key).HasColumnName("key").IsRequired(); e.Property(x => x.Title).HasColumnName("title"); e.Property(x => x.LocalizedTitle).HasColumnName("localized_title").HasColumnType("jsonb"); e.Property(x => x.Hint).HasColumnName("hint"); e.Property(x => x.Type).HasColumnName("type").IsRequired(); e.Property(x => x.Value).HasColumnName("value"); e.Property(x => x.ValueFileId).HasColumnName("value_file_id"); e.Property(x => x.InsertedFileType).HasColumnName("inserted_file_type"); e.Property(x => x.FileUrlCached).HasColumnName("file_url_cached"); e.Property(x => x.FileUrlCachedAt).HasColumnName("file_url_cached_at"); e.Property(x => x.FontFace).HasColumnName("font_face"); e.Property(x => x.FontFaceName).HasColumnName("font_face_name"); e.Property(x => x.FontSize).HasColumnName("font_size"); e.Property(x => x.DefaultFontSize).HasColumnName("default_font_size"); e.Property(x => x.DefaultFontFace).HasColumnName("default_font_face"); e.Property(x => x.Justify).HasColumnName("justify"); e.Property(x => x.PositionInContainer).HasColumnName("position_in_container"); e.Property(x => x.DirectionLayerValue).HasColumnName("direction_layer_value"); e.Property(x => x.IsTextBox).HasColumnName("is_text_box"); e.Property(x => x.AiInputType).HasColumnName("ai_input_type"); e.Property(x => x.SelectedDp).HasColumnName("selected_dp"); e.Property(x => x.RepeaterItemKey).HasColumnName("repeater_item_key"); e.Property(x => x.RepeaterIndex).HasColumnName("repeater_index"); e.Property(x => x.IsFocused).HasColumnName("is_focused"); e.Property(x => x.Status).HasColumnName("status"); e.Property(x => x.MappedList).HasColumnName("mapped_list").HasColumnType("jsonb"); e.Property(x => x.Thumbnail).HasColumnName("thumbnail"); e.Property(x => x.Sort).HasColumnName("sort"); e.Property(x => x.CreatedAt).HasColumnName("created_at"); e.Property(x => x.UpdatedAt).HasColumnName("updated_at"); e.HasOne(x => x.SavedScene).WithMany(x => x.Contents).HasForeignKey(x => x.SavedSceneId).OnDelete(DeleteBehavior.Cascade); }); mb.Entity(e => { e.ToTable("saved_scene_colors"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id").UseIdentityByDefaultColumn(); e.Property(x => x.SavedSceneId).HasColumnName("saved_scene_id"); e.Property(x => x.ElementKey).HasColumnName("element_key").IsRequired(); e.Property(x => x.Title).HasColumnName("title"); e.Property(x => x.Icon).HasColumnName("icon"); e.Property(x => x.AttrValue).HasColumnName("attr_value").IsRequired(); e.Property(x => x.Value).HasColumnName("value").IsRequired(); e.Property(x => x.IsSelected).HasColumnName("is_selected"); e.Property(x => x.Sort).HasColumnName("sort"); e.HasOne(x => x.SavedScene).WithMany(x => x.Colors).HasForeignKey(x => x.SavedSceneId).OnDelete(DeleteBehavior.Cascade); }); mb.Entity(e => { e.ToTable("saved_scene_color_presets"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id").UseIdentityByDefaultColumn(); e.Property(x => x.SavedSceneId).HasColumnName("saved_scene_id"); e.Property(x => x.IsSelected).HasColumnName("is_selected"); e.Property(x => x.Sort).HasColumnName("sort"); e.HasOne(x => x.SavedScene).WithMany(x => x.ColorPresets).HasForeignKey(x => x.SavedSceneId).OnDelete(DeleteBehavior.Cascade); }); mb.Entity(e => { e.ToTable("saved_scene_color_preset_items"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id").UseIdentityByDefaultColumn(); e.Property(x => x.PresetId).HasColumnName("preset_id"); e.Property(x => x.ElementKey).HasColumnName("element_key").IsRequired(); e.Property(x => x.Value).HasColumnName("value").IsRequired(); e.Property(x => x.Sort).HasColumnName("sort"); e.HasOne(x => x.Preset).WithMany(x => x.Items).HasForeignKey(x => x.PresetId).OnDelete(DeleteBehavior.Cascade); }); mb.Entity(e => { e.ToTable("saved_scene_characters"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id").UseIdentityByDefaultColumn(); e.Property(x => x.SavedSceneId).HasColumnName("saved_scene_id"); e.Property(x => x.Key).HasColumnName("key"); e.Property(x => x.Name).HasColumnName("name"); e.Property(x => x.Icon).HasColumnName("icon"); e.HasOne(x => x.SavedScene).WithMany(x => x.Characters).HasForeignKey(x => x.SavedSceneId).OnDelete(DeleteBehavior.Cascade); }); mb.Entity(e => { e.ToTable("saved_scene_character_controllers"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id").UseIdentityByDefaultColumn(); e.Property(x => x.SavedSceneCharacterId).HasColumnName("saved_scene_character_id"); e.Property(x => x.Name).HasColumnName("name"); e.Property(x => x.Key).HasColumnName("key").IsRequired(); e.Property(x => x.Value).HasColumnName("value").IsRequired(); e.Property(x => x.Sort).HasColumnName("sort"); e.HasOne(x => x.Character).WithMany(x => x.Controllers).HasForeignKey(x => x.SavedSceneCharacterId).OnDelete(DeleteBehavior.Cascade); }); } private static void ConfigureSharedData(ModelBuilder mb) { mb.Entity(e => { e.ToTable("saved_shared_colors"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id").UseIdentityByDefaultColumn(); e.Property(x => x.SavedProjectId).HasColumnName("saved_project_id"); e.Property(x => x.ElementKey).HasColumnName("element_key").IsRequired(); e.Property(x => x.Title).HasColumnName("title"); e.Property(x => x.Icon).HasColumnName("icon"); e.Property(x => x.AttrValue).HasColumnName("attr_value").IsRequired(); e.Property(x => x.Value).HasColumnName("value").IsRequired(); e.Property(x => x.IsSelected).HasColumnName("is_selected"); e.Property(x => x.Sort).HasColumnName("sort"); e.HasOne(x => x.SavedProject).WithMany(x => x.SharedColors).HasForeignKey(x => x.SavedProjectId).OnDelete(DeleteBehavior.Cascade); }); mb.Entity(e => { e.ToTable("saved_shared_color_presets"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id").UseIdentityByDefaultColumn(); e.Property(x => x.SavedProjectId).HasColumnName("saved_project_id"); e.Property(x => x.Name).HasColumnName("name"); e.Property(x => x.IsSelected).HasColumnName("is_selected"); e.Property(x => x.Sort).HasColumnName("sort"); e.HasOne(x => x.SavedProject).WithMany(x => x.SharedColorPresets).HasForeignKey(x => x.SavedProjectId).OnDelete(DeleteBehavior.Cascade); }); mb.Entity(e => { e.ToTable("saved_shared_color_preset_items"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id").UseIdentityByDefaultColumn(); e.Property(x => x.PresetId).HasColumnName("preset_id"); e.Property(x => x.ElementKey).HasColumnName("element_key").IsRequired(); e.Property(x => x.Value).HasColumnName("value").IsRequired(); e.Property(x => x.Sort).HasColumnName("sort"); e.HasOne(x => x.Preset).WithMany(x => x.Items).HasForeignKey(x => x.PresetId).OnDelete(DeleteBehavior.Cascade); }); mb.Entity(e => { e.ToTable("saved_shared_layers"); e.HasKey(x => x.Id); e.Property(x => x.Id).HasColumnName("id").UseIdentityByDefaultColumn(); e.Property(x => x.SavedProjectId).HasColumnName("saved_project_id"); e.Property(x => x.Key).HasColumnName("key").IsRequired(); e.Property(x => x.Title).HasColumnName("title"); e.Property(x => x.LocalizedTitle).HasColumnName("localized_title").HasColumnType("jsonb"); e.Property(x => x.Hint).HasColumnName("hint"); e.Property(x => x.Type).HasColumnName("type").IsRequired(); e.Property(x => x.Value).HasColumnName("value"); e.Property(x => x.ValueFileId).HasColumnName("value_file_id"); e.Property(x => x.FileUrlCached).HasColumnName("file_url_cached"); e.Property(x => x.FileUrlCachedAt).HasColumnName("file_url_cached_at"); e.Property(x => x.FontFace).HasColumnName("font_face"); e.Property(x => x.FontFaceName).HasColumnName("font_face_name"); e.Property(x => x.FontSize).HasColumnName("font_size"); e.Property(x => x.DefaultFontSize).HasColumnName("default_font_size"); e.Property(x => x.DefaultFontFace).HasColumnName("default_font_face"); e.Property(x => x.Justify).HasColumnName("justify"); e.Property(x => x.PositionInContainer).HasColumnName("position_in_container"); e.Property(x => x.DirectionLayerValue).HasColumnName("direction_layer_value"); e.Property(x => x.IsTextBox).HasColumnName("is_text_box"); e.Property(x => x.AiInputType).HasColumnName("ai_input_type"); e.Property(x => x.MappedList).HasColumnName("mapped_list").HasColumnType("jsonb"); e.Property(x => x.Thumbnail).HasColumnName("thumbnail"); e.Property(x => x.Width).HasColumnName("width"); e.Property(x => x.Height).HasColumnName("height"); e.Property(x => x.MinDurationSec).HasColumnName("min_duration_sec"); e.Property(x => x.MaxDurationSec).HasColumnName("max_duration_sec"); e.Property(x => x.IsFocused).HasColumnName("is_focused"); e.Property(x => x.IsFontChangeable).HasColumnName("is_font_changeable"); e.Property(x => x.IsFontSizeChangeable).HasColumnName("is_font_size_changeable"); e.Property(x => x.Status).HasColumnName("status"); e.Property(x => x.Sort).HasColumnName("sort"); e.Property(x => x.CreatedAt).HasColumnName("created_at"); e.Property(x => x.UpdatedAt).HasColumnName("updated_at"); e.HasOne(x => x.SavedProject).WithMany(x => x.SharedLayers).HasForeignKey(x => x.SavedProjectId).OnDelete(DeleteBehavior.Cascade); }); } }