diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 9d1f2f21..0fbf76a1 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -600,13 +600,9 @@ namespace Emby.Server.Implementations FindParts(); - // Ensure at least one user exists for the startup wizard + // Ensure at least one user exists var userManager = Resolve(); - if (!ConfigurationManager.Configuration.IsStartupWizardCompleted) - { - Logger.LogInformation("Startup wizard not completed, ensuring initial user exists"); - await userManager.InitializeAsync().ConfigureAwait(false); - } + await userManager.InitializeAsync().ConfigureAwait(false); } private X509Certificate2 GetCertificate(string path, string password) diff --git a/Jellyfin.Api/Controllers/StartupController.cs b/Jellyfin.Api/Controllers/StartupController.cs index cb80e5e0..f26dc9ed 100644 --- a/Jellyfin.Api/Controllers/StartupController.cs +++ b/Jellyfin.Api/Controllers/StartupController.cs @@ -9,6 +9,7 @@ using System.Linq; using System.Threading.Tasks; using Jellyfin.Api.Constants; using Jellyfin.Api.Models.StartupDtos; +using Jellyfin.Data; using MediaBrowser.Common.Api; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; @@ -111,15 +112,18 @@ public class StartupController : BaseJellyfinApiController [HttpGet("User")] [HttpGet("FirstUser", Name = "GetFirstUser_2")] [ProducesResponseType(StatusCodes.Status200OK)] - public async Task GetFirstUser() + public Task GetFirstUser() { - // TODO: Remove this method when startup wizard no longer requires an existing user. - await _userManager.InitializeAsync().ConfigureAwait(false); - var user = _userManager.Users.First(); - return new StartupUserDto + var user = _userManager.Users.FirstOrDefault(); + if (user is null) + { + return Task.FromResult(null); + } + + return Task.FromResult(new StartupUserDto { Name = user.Username - }; + }); } /// @@ -135,20 +139,33 @@ public class StartupController : BaseJellyfinApiController [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task UpdateStartupUser([FromBody] StartupUserDto startupUserDto) { - await _userManager.InitializeAsync().ConfigureAwait(false); - var user = _userManager.Users.First(); if (string.IsNullOrWhiteSpace(startupUserDto.Password)) { return BadRequest("Password must not be empty"); } - if (startupUserDto.Name is not null) + var user = _userManager.Users.FirstOrDefault(); + + // If no user exists, create the initial admin user + if (user is null) + { + if (string.IsNullOrWhiteSpace(startupUserDto.Name)) + { + return BadRequest("Username must not be empty when creating initial user"); + } + + user = await _userManager.CreateUserAsync(startupUserDto.Name).ConfigureAwait(false); + user.SetPermission(Jellyfin.Database.Implementations.Enums.PermissionKind.IsAdministrator, true); + user.SetPermission(Jellyfin.Database.Implementations.Enums.PermissionKind.EnableContentDeletion, true); + user.SetPermission(Jellyfin.Database.Implementations.Enums.PermissionKind.EnableRemoteControlOfOtherUsers, true); + await _userManager.UpdateUserAsync(user).ConfigureAwait(false); + } + else if (startupUserDto.Name is not null) { user.Username = startupUserDto.Name; + await _userManager.UpdateUserAsync(user).ConfigureAwait(false); } - await _userManager.UpdateUserAsync(user).ConfigureAwait(false); - if (!string.IsNullOrEmpty(startupUserDto.Password)) { await _userManager.ChangePassword(user, startupUserDto.Password).ConfigureAwait(false); diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index f430496a..bce95403 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -542,24 +542,18 @@ namespace Jellyfin.Server.Implementations.Users /// public async Task InitializeAsync() { - // TODO: Refactor the startup wizard so that it doesn't require a user to already exist. + // If no users exist, create the default admin user if (_users.Any()) { return; } - var defaultName = Environment.UserName; - if (string.IsNullOrWhiteSpace(defaultName) || !ValidUsernameRegex().IsMatch(defaultName)) - { - defaultName = "MyJellyfinUser"; - } - - _logger.LogWarning("No users, creating one with username {UserName}", defaultName); + _logger.LogWarning("No users found, creating default admin user with username 'admin'"); var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); await using (dbContext.ConfigureAwait(false)) { - var newUser = await CreateUserInternalAsync(defaultName, dbContext).ConfigureAwait(false); + var newUser = await CreateUserInternalAsync("admin", dbContext).ConfigureAwait(false); newUser.SetPermission(PermissionKind.IsAdministrator, true); newUser.SetPermission(PermissionKind.EnableContentDeletion, true); newUser.SetPermission(PermissionKind.EnableRemoteControlOfOtherUsers, true); @@ -578,6 +572,11 @@ namespace Jellyfin.Server.Implementations.Users .ConfigureAwait(false); _users.Add(reloadedUser.Id, reloadedUser); + + // Set the default password "jellyfin" + await ChangePassword(reloadedUser, "jellyfin").ConfigureAwait(false); + + _logger.LogWarning("Default admin user created with username 'admin' and password 'jellyfin'"); } } diff --git a/Jellyfin.Server/Properties/PublishProfiles/FolderProfile.pubxml.user b/Jellyfin.Server/Properties/PublishProfiles/FolderProfile.pubxml.user index 9a0d94c9..e6ccac0f 100644 --- a/Jellyfin.Server/Properties/PublishProfiles/FolderProfile.pubxml.user +++ b/Jellyfin.Server/Properties/PublishProfiles/FolderProfile.pubxml.user @@ -3,7 +3,7 @@ <_PublishTargetUrl>E:\Program Files\Jellyfin - True|2026-02-23T20:36:20.8511403Z||;True|2026-02-23T14:06:04.7110322-05:00||;True|2026-02-23T13:51:39.0686004-05:00||;True|2026-02-23T13:12:08.3890386-05:00||;False|2026-02-23T13:06:12.0188266-05:00||;True|2026-02-23T12:08:36.6307546-05:00||;False|2026-02-23T12:02:39.0223565-05:00||;True|2026-02-23T11:48:21.1980918-05:00||;True|2026-02-23T11:13:01.1928466-05:00||;True|2026-02-23T10:32:13.7989634-05:00||;True|2026-02-23T09:02:53.5227868-05:00||;True|2026-02-23T08:11:05.7403694-05:00||;False|2026-02-23T08:07:46.6244256-05:00||;True|2026-02-23T08:00:05.0454829-05:00||;False|2026-02-23T07:56:44.6461434-05:00||;True|2026-02-23T07:50:44.1212024-05:00||;True|2026-02-23T07:29:19.3226285-05:00||;True|2026-02-22T20:02:08.7802640-05:00||;True|2026-02-22T19:00:49.1033285-05:00||;True|2026-02-22T18:46:50.8399883-05:00||;True|2026-02-22T18:36:34.2983199-05:00||;True|2026-02-22T18:33:05.9495883-05:00||;True|2026-02-22T18:28:09.3531365-05:00||;True|2026-02-22T18:23:19.7767548-05:00||;True|2026-02-22T18:03:49.9702878-05:00||;True|2026-02-22T17:31:43.8027839-05:00||;True|2026-02-22T17:14:22.1722691-05:00||;True|2026-02-22T17:07:09.6937759-05:00||;True|2026-02-22T16:29:37.5643134-05:00||;True|2026-02-22T16:05:05.3412117-05:00||;True|2026-02-22T15:59:39.7645693-05:00||; + True|2026-02-23T22:50:19.2488617Z||;True|2026-02-23T17:40:34.7963081-05:00||;True|2026-02-23T17:30:42.0185573-05:00||;False|2026-02-23T16:44:17.4036787-05:00||;False|2026-02-23T16:10:39.9498056-05:00||;False|2026-02-23T16:06:47.9500254-05:00||;True|2026-02-23T15:36:20.8511403-05:00||;True|2026-02-23T14:06:04.7110322-05:00||;True|2026-02-23T13:51:39.0686004-05:00||;True|2026-02-23T13:12:08.3890386-05:00||;False|2026-02-23T13:06:12.0188266-05:00||;True|2026-02-23T12:08:36.6307546-05:00||;False|2026-02-23T12:02:39.0223565-05:00||;True|2026-02-23T11:48:21.1980918-05:00||;True|2026-02-23T11:13:01.1928466-05:00||;True|2026-02-23T10:32:13.7989634-05:00||;True|2026-02-23T09:02:53.5227868-05:00||;True|2026-02-23T08:11:05.7403694-05:00||;False|2026-02-23T08:07:46.6244256-05:00||;True|2026-02-23T08:00:05.0454829-05:00||;False|2026-02-23T07:56:44.6461434-05:00||;True|2026-02-23T07:50:44.1212024-05:00||;True|2026-02-23T07:29:19.3226285-05:00||;True|2026-02-22T20:02:08.7802640-05:00||;True|2026-02-22T19:00:49.1033285-05:00||;True|2026-02-22T18:46:50.8399883-05:00||;True|2026-02-22T18:36:34.2983199-05:00||;True|2026-02-22T18:33:05.9495883-05:00||;True|2026-02-22T18:28:09.3531365-05:00||;True|2026-02-22T18:23:19.7767548-05:00||;True|2026-02-22T18:03:49.9702878-05:00||;True|2026-02-22T17:31:43.8027839-05:00||;True|2026-02-22T17:14:22.1722691-05:00||;True|2026-02-22T17:07:09.6937759-05:00||;True|2026-02-22T16:29:37.5643134-05:00||;True|2026-02-22T16:05:05.3412117-05:00||;True|2026-02-22T15:59:39.7645693-05:00||; \ No newline at end of file diff --git a/src/Jellyfin.CodeAnalysis/bin/Debug/netstandard2.0/Jellyfin.CodeAnalysis.dll b/src/Jellyfin.CodeAnalysis/bin/Debug/netstandard2.0/Jellyfin.CodeAnalysis.dll index d7f87adc..2df42d2a 100644 Binary files a/src/Jellyfin.CodeAnalysis/bin/Debug/netstandard2.0/Jellyfin.CodeAnalysis.dll and b/src/Jellyfin.CodeAnalysis/bin/Debug/netstandard2.0/Jellyfin.CodeAnalysis.dll differ diff --git a/src/Jellyfin.CodeAnalysis/bin/Debug/netstandard2.0/Jellyfin.CodeAnalysis.pdb b/src/Jellyfin.CodeAnalysis/bin/Debug/netstandard2.0/Jellyfin.CodeAnalysis.pdb index 9b4b64e5..dbe9a433 100644 Binary files a/src/Jellyfin.CodeAnalysis/bin/Debug/netstandard2.0/Jellyfin.CodeAnalysis.pdb and b/src/Jellyfin.CodeAnalysis/bin/Debug/netstandard2.0/Jellyfin.CodeAnalysis.pdb differ diff --git a/src/Jellyfin.CodeAnalysis/obj/Debug/netstandard2.0/Jellyfin.CodeAnalysis.AssemblyInfo.cs b/src/Jellyfin.CodeAnalysis/obj/Debug/netstandard2.0/Jellyfin.CodeAnalysis.AssemblyInfo.cs index 45f3ffe7..0f504abe 100644 --- a/src/Jellyfin.CodeAnalysis/obj/Debug/netstandard2.0/Jellyfin.CodeAnalysis.AssemblyInfo.cs +++ b/src/Jellyfin.CodeAnalysis/obj/Debug/netstandard2.0/Jellyfin.CodeAnalysis.AssemblyInfo.cs @@ -14,7 +14,7 @@ using System.Reflection; [assembly: System.Reflection.AssemblyCompanyAttribute("Jellyfin.CodeAnalysis")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+f833f0ceebcaa4ac5d8972494fbe574ff6b0036b")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+534b0cde918839aca165b7bd2b1c7df761aca82c")] [assembly: System.Reflection.AssemblyProductAttribute("Jellyfin.CodeAnalysis")] [assembly: System.Reflection.AssemblyTitleAttribute("Jellyfin.CodeAnalysis")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] diff --git a/src/Jellyfin.CodeAnalysis/obj/Debug/netstandard2.0/Jellyfin.CodeAnalysis.AssemblyInfoInputs.cache b/src/Jellyfin.CodeAnalysis/obj/Debug/netstandard2.0/Jellyfin.CodeAnalysis.AssemblyInfoInputs.cache index ff06f352..129fe210 100644 --- a/src/Jellyfin.CodeAnalysis/obj/Debug/netstandard2.0/Jellyfin.CodeAnalysis.AssemblyInfoInputs.cache +++ b/src/Jellyfin.CodeAnalysis/obj/Debug/netstandard2.0/Jellyfin.CodeAnalysis.AssemblyInfoInputs.cache @@ -1 +1 @@ -ec635030130ae48b7e69fa591520d48e411bcd6177f6760b1e8b737bdcf85112 +16d1d32a5e6e4b7472006b4665d10d7735bf11c7602c8304312d34d2b63afa7f diff --git a/src/Jellyfin.CodeAnalysis/obj/Debug/netstandard2.0/Jellyfin.CodeAnalysis.dll b/src/Jellyfin.CodeAnalysis/obj/Debug/netstandard2.0/Jellyfin.CodeAnalysis.dll index d7f87adc..2df42d2a 100644 Binary files a/src/Jellyfin.CodeAnalysis/obj/Debug/netstandard2.0/Jellyfin.CodeAnalysis.dll and b/src/Jellyfin.CodeAnalysis/obj/Debug/netstandard2.0/Jellyfin.CodeAnalysis.dll differ diff --git a/src/Jellyfin.CodeAnalysis/obj/Debug/netstandard2.0/Jellyfin.CodeAnalysis.pdb b/src/Jellyfin.CodeAnalysis/obj/Debug/netstandard2.0/Jellyfin.CodeAnalysis.pdb index 9b4b64e5..dbe9a433 100644 Binary files a/src/Jellyfin.CodeAnalysis/obj/Debug/netstandard2.0/Jellyfin.CodeAnalysis.pdb and b/src/Jellyfin.CodeAnalysis/obj/Debug/netstandard2.0/Jellyfin.CodeAnalysis.pdb differ diff --git a/src/Jellyfin.Database/Jellyfin.Database.Implementations/ModelConfiguration/HomeSectionConfiguration.cs b/src/Jellyfin.Database/Jellyfin.Database.Implementations/ModelConfiguration/HomeSectionConfiguration.cs new file mode 100644 index 00000000..55512121 --- /dev/null +++ b/src/Jellyfin.Database/Jellyfin.Database.Implementations/ModelConfiguration/HomeSectionConfiguration.cs @@ -0,0 +1,37 @@ +// +// Copyright (c) PlaceholderCompany. All rights reserved. +// + +namespace Jellyfin.Database.Implementations.ModelConfiguration +{ + using System; + using Jellyfin.Database.Implementations.Entities; + using Microsoft.EntityFrameworkCore; + using Microsoft.EntityFrameworkCore.Metadata.Builders; + + /// + /// FluentAPI configuration for the HomeSection entity. + /// + public class HomeSectionConfiguration : IEntityTypeConfiguration + { + /// + public void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + // Explicitly set table name to singular to match the migration + builder.ToTable("HomeSection", "displaypreferences"); + + builder.HasKey(e => e.Id); + + builder.Property(e => e.Order) + .IsRequired(); + + builder.Property(e => e.Type) + .IsRequired(); + + builder.Property(e => e.DisplayPreferencesId) + .IsRequired(); + } + } +}