43cdbf9b35
Refactored user initialization to always ensure at least one user exists at startup. If no users are present, a default admin user (admin/jellyfin) is created automatically. Updated StartupController endpoints to handle cases where no user exists. Simplified UserManager.InitializeAsync logic. Added HomeSectionConfiguration for EF Core entity mapping. Includes minor project and assembly info updates.
177 lines
6.9 KiB
C#
177 lines
6.9 KiB
C#
// <copyright file="StartupController.cs" company="PlaceholderCompany">
|
|
// Copyright (c) PlaceholderCompany. All rights reserved.
|
|
// </copyright>
|
|
|
|
namespace Jellyfin.Api.Controllers;
|
|
|
|
using System.ComponentModel.DataAnnotations;
|
|
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;
|
|
using MediaBrowser.Controller.Library;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
|
/// <summary>
|
|
/// The startup wizard controller.
|
|
/// </summary>
|
|
[Authorize(Policy = Policies.FirstTimeSetupOrElevated)]
|
|
public class StartupController : BaseJellyfinApiController
|
|
{
|
|
private readonly IServerConfigurationManager _config;
|
|
private readonly IUserManager _userManager;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="StartupController" /> class.
|
|
/// </summary>
|
|
/// <param name="config">The server configuration manager.</param>
|
|
/// <param name="userManager">The user manager.</param>
|
|
public StartupController(IServerConfigurationManager config, IUserManager userManager)
|
|
{
|
|
_config = config;
|
|
_userManager = userManager;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Completes the startup wizard.
|
|
/// </summary>
|
|
/// <response code="204">Startup wizard completed.</response>
|
|
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
|
[HttpPost("Complete")]
|
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
|
public ActionResult CompleteWizard()
|
|
{
|
|
_config.Configuration.IsStartupWizardCompleted = true;
|
|
_config.SaveConfiguration();
|
|
return NoContent();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the initial startup wizard configuration.
|
|
/// </summary>
|
|
/// <response code="200">Initial startup wizard configuration retrieved.</response>
|
|
/// <returns>An <see cref="OkResult"/> containing the initial startup wizard configuration.</returns>
|
|
[HttpGet("Configuration")]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
public ActionResult<StartupConfigurationDto> GetStartupConfiguration()
|
|
{
|
|
return new StartupConfigurationDto
|
|
{
|
|
ServerName = _config.Configuration.ServerName,
|
|
UICulture = _config.Configuration.UICulture,
|
|
MetadataCountryCode = _config.Configuration.MetadataCountryCode,
|
|
PreferredMetadataLanguage = _config.Configuration.PreferredMetadataLanguage
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the initial startup wizard configuration.
|
|
/// </summary>
|
|
/// <param name="startupConfiguration">The updated startup configuration.</param>
|
|
/// <response code="204">Configuration saved.</response>
|
|
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
|
[HttpPost("Configuration")]
|
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
|
public ActionResult UpdateInitialConfiguration([FromBody, Required] StartupConfigurationDto startupConfiguration)
|
|
{
|
|
_config.Configuration.ServerName = startupConfiguration.ServerName ?? string.Empty;
|
|
_config.Configuration.UICulture = startupConfiguration.UICulture ?? string.Empty;
|
|
_config.Configuration.MetadataCountryCode = startupConfiguration.MetadataCountryCode ?? string.Empty;
|
|
_config.Configuration.PreferredMetadataLanguage = startupConfiguration.PreferredMetadataLanguage ?? string.Empty;
|
|
_config.SaveConfiguration();
|
|
return NoContent();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets remote access and UPnP.
|
|
/// </summary>
|
|
/// <param name="startupRemoteAccessDto">The startup remote access dto.</param>
|
|
/// <response code="204">Configuration saved.</response>
|
|
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
|
|
[HttpPost("RemoteAccess")]
|
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
|
public ActionResult SetRemoteAccess([FromBody, Required] StartupRemoteAccessDto startupRemoteAccessDto)
|
|
{
|
|
NetworkConfiguration settings = _config.GetNetworkConfiguration();
|
|
settings.EnableRemoteAccess = startupRemoteAccessDto.EnableRemoteAccess;
|
|
_config.SaveConfiguration(NetworkConfigurationStore.StoreKey, settings);
|
|
return NoContent();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the first user.
|
|
/// </summary>
|
|
/// <response code="200">Initial user retrieved.</response>
|
|
/// <returns>The first user.</returns>
|
|
[HttpGet("User")]
|
|
[HttpGet("FirstUser", Name = "GetFirstUser_2")]
|
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
|
public Task<StartupUserDto?> GetFirstUser()
|
|
{
|
|
var user = _userManager.Users.FirstOrDefault();
|
|
if (user is null)
|
|
{
|
|
return Task.FromResult<StartupUserDto?>(null);
|
|
}
|
|
|
|
return Task.FromResult<StartupUserDto?>(new StartupUserDto
|
|
{
|
|
Name = user.Username
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the user name and password.
|
|
/// </summary>
|
|
/// <param name="startupUserDto">The DTO containing username and password.</param>
|
|
/// <response code="204">Updated user name and password.</response>
|
|
/// <returns>
|
|
/// A <see cref="Task" /> that represents the asynchronous update operation.
|
|
/// The task result contains a <see cref="NoContentResult"/> indicating success.
|
|
/// </returns>
|
|
[HttpPost("User")]
|
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
|
public async Task<ActionResult> UpdateStartupUser([FromBody] StartupUserDto startupUserDto)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(startupUserDto.Password))
|
|
{
|
|
return BadRequest("Password must not be empty");
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(startupUserDto.Password))
|
|
{
|
|
await _userManager.ChangePassword(user, startupUserDto.Password).ConfigureAwait(false);
|
|
}
|
|
|
|
return NoContent();
|
|
}
|
|
}
|