Refactored css classes to match flyonui. Switched to postgres arrays for permissions. Migrated file manager. Adjusted everything to work with the latest mooncore version
This commit is contained in:
@@ -45,7 +45,7 @@ public class ApiKeysController : Controller
|
||||
.Select(x => new ApiKeyResponse()
|
||||
{
|
||||
Id = x.Id,
|
||||
PermissionsJson = x.PermissionsJson,
|
||||
Permissions = x.Permissions,
|
||||
Description = x.Description,
|
||||
ExpiresAt = x.ExpiresAt
|
||||
})
|
||||
@@ -75,7 +75,7 @@ public class ApiKeysController : Controller
|
||||
return new ApiKeyResponse()
|
||||
{
|
||||
Id = apiKey.Id,
|
||||
PermissionsJson = apiKey.PermissionsJson,
|
||||
Permissions = apiKey.Permissions,
|
||||
Description = apiKey.Description,
|
||||
ExpiresAt = apiKey.ExpiresAt
|
||||
};
|
||||
@@ -88,7 +88,7 @@ public class ApiKeysController : Controller
|
||||
var apiKey = new ApiKey()
|
||||
{
|
||||
Description = request.Description,
|
||||
PermissionsJson = request.PermissionsJson,
|
||||
Permissions = request.Permissions,
|
||||
ExpiresAt = request.ExpiresAt
|
||||
};
|
||||
|
||||
@@ -97,7 +97,7 @@ public class ApiKeysController : Controller
|
||||
var response = new CreateApiKeyResponse
|
||||
{
|
||||
Id = finalApiKey.Id,
|
||||
PermissionsJson = finalApiKey.PermissionsJson,
|
||||
Permissions = finalApiKey.Permissions,
|
||||
Description = finalApiKey.Description,
|
||||
ExpiresAt = finalApiKey.ExpiresAt,
|
||||
Secret = ApiKeyService.GenerateJwt(finalApiKey)
|
||||
@@ -125,7 +125,7 @@ public class ApiKeysController : Controller
|
||||
{
|
||||
Id = apiKey.Id,
|
||||
Description = apiKey.Description,
|
||||
PermissionsJson = apiKey.PermissionsJson,
|
||||
Permissions = apiKey.Permissions,
|
||||
ExpiresAt = apiKey.ExpiresAt
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,8 +17,40 @@ namespace Moonlight.ApiServer.Http.Controllers.Admin.Sys;
|
||||
public class FilesController : Controller
|
||||
{
|
||||
private readonly string BaseDirectory = "storage";
|
||||
private readonly long ChunkSize = ByteConverter.FromMegaBytes(20).Bytes;
|
||||
private readonly long MaxChunkSize = ByteConverter.FromMegaBytes(20).Bytes;
|
||||
|
||||
[HttpPost("touch")]
|
||||
public async Task CreateFile([FromQuery] string path)
|
||||
{
|
||||
var safePath = SanitizePath(path);
|
||||
var physicalPath = Path.Combine(BaseDirectory, safePath);
|
||||
|
||||
if (System.IO.File.Exists(physicalPath))
|
||||
throw new HttpApiException("A file already exists at that path", 400);
|
||||
|
||||
if (Directory.Exists(path))
|
||||
throw new HttpApiException("A folder already exists at that path", 400);
|
||||
|
||||
await using var fs = System.IO.File.Create(physicalPath);
|
||||
fs.Close();
|
||||
}
|
||||
|
||||
[HttpPost("mkdir")]
|
||||
public Task CreateFolder([FromQuery] string path)
|
||||
{
|
||||
var safePath = SanitizePath(path);
|
||||
var physicalPath = Path.Combine(BaseDirectory, safePath);
|
||||
|
||||
if (Directory.Exists(path))
|
||||
throw new HttpApiException("A folder already exists at that path", 400);
|
||||
|
||||
if (System.IO.File.Exists(physicalPath))
|
||||
throw new HttpApiException("A file already exists at that path", 400);
|
||||
|
||||
Directory.CreateDirectory(physicalPath);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
[HttpGet("list")]
|
||||
public Task<FileSystemEntryResponse[]> List([FromQuery] string path)
|
||||
{
|
||||
@@ -38,7 +70,7 @@ public class FilesController : Controller
|
||||
Name = fi.Name,
|
||||
Size = fi.Length,
|
||||
CreatedAt = fi.CreationTimeUtc,
|
||||
IsFile = true,
|
||||
IsFolder = false,
|
||||
UpdatedAt = fi.LastWriteTimeUtc
|
||||
});
|
||||
}
|
||||
@@ -55,7 +87,7 @@ public class FilesController : Controller
|
||||
Size = 0,
|
||||
CreatedAt = di.CreationTimeUtc,
|
||||
UpdatedAt = di.LastWriteTimeUtc,
|
||||
IsFile = false
|
||||
IsFolder = true
|
||||
});
|
||||
}
|
||||
|
||||
@@ -65,23 +97,23 @@ public class FilesController : Controller
|
||||
}
|
||||
|
||||
[HttpPost("upload")]
|
||||
public async Task Upload([FromQuery] string path, [FromQuery] long totalSize, [FromQuery] int chunkId)
|
||||
public async Task Upload([FromQuery] string path, [FromQuery] long chunkSize, [FromQuery] long totalSize, [FromQuery] int chunkId)
|
||||
{
|
||||
if (Request.Form.Files.Count != 1)
|
||||
throw new HttpApiException("You need to provide exactly one file", 400);
|
||||
|
||||
var file = Request.Form.Files[0];
|
||||
|
||||
if (file.Length > ChunkSize)
|
||||
if (file.Length > chunkSize)
|
||||
throw new HttpApiException("The provided data exceeds the chunk size limit", 400);
|
||||
|
||||
var chunks = totalSize / ChunkSize;
|
||||
chunks += totalSize % ChunkSize > 0 ? 1 : 0;
|
||||
var chunks = totalSize / chunkSize;
|
||||
chunks += totalSize % chunkSize > 0 ? 1 : 0;
|
||||
|
||||
if (chunkId > chunks)
|
||||
throw new HttpApiException("Invalid chunk id: Out of bounds", 400);
|
||||
|
||||
var positionToSkipTo = ChunkSize * chunkId;
|
||||
var positionToSkipTo = chunkSize * chunkId;
|
||||
|
||||
var safePath = SanitizePath(path);
|
||||
var physicalPath = Path.Combine(BaseDirectory, safePath);
|
||||
@@ -156,16 +188,6 @@ public class FilesController : Controller
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
[HttpPost("mkdir")]
|
||||
public Task CreateDirectory([FromQuery] string path)
|
||||
{
|
||||
var safePath = SanitizePath(path);
|
||||
var physicalPath = Path.Combine(BaseDirectory, safePath);
|
||||
|
||||
Directory.CreateDirectory(physicalPath);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
[HttpGet("download")]
|
||||
public async Task Download([FromQuery] string path)
|
||||
{
|
||||
@@ -431,5 +453,23 @@ public class FilesController : Controller
|
||||
#endregion
|
||||
|
||||
private string SanitizePath(string path)
|
||||
=> path.Replace("..", "");
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
return string.Empty;
|
||||
|
||||
// Normalize separators
|
||||
path = path.Replace('\\', '/');
|
||||
|
||||
// Remove ".." and "."
|
||||
var parts = path.Split('/', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Where(part => part != ".." && part != ".");
|
||||
|
||||
var sanitized = string.Join("/", parts);
|
||||
|
||||
// Ensure it does not start with a slash
|
||||
if (sanitized.StartsWith('/'))
|
||||
sanitized = sanitized.TrimStart('/');
|
||||
|
||||
return sanitized;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MoonCore.Attributes;
|
||||
using Moonlight.ApiServer.Interfaces;
|
||||
using Moonlight.ApiServer.Services;
|
||||
using Moonlight.Shared.Http.Responses.Admin.Sys;
|
||||
|
||||
@@ -12,13 +10,10 @@ namespace Moonlight.ApiServer.Http.Controllers.Admin.Sys;
|
||||
public class SystemController : Controller
|
||||
{
|
||||
private readonly ApplicationService ApplicationService;
|
||||
private readonly IEnumerable<IDiagnoseProvider> DiagnoseProviders;
|
||||
|
||||
|
||||
public SystemController(ApplicationService applicationService, IEnumerable<IDiagnoseProvider> diagnoseProviders)
|
||||
|
||||
public SystemController(ApplicationService applicationService)
|
||||
{
|
||||
ApplicationService = applicationService;
|
||||
DiagnoseProviders = diagnoseProviders;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
|
||||
@@ -45,7 +45,7 @@ public class UsersController : Controller
|
||||
Id = x.Id,
|
||||
Email = x.Email,
|
||||
Username = x.Username,
|
||||
PermissionsJson = x.PermissionsJson
|
||||
Permissions = x.Permissions
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
@@ -75,7 +75,7 @@ public class UsersController : Controller
|
||||
Id = user.Id,
|
||||
Email = user.Email,
|
||||
Username = user.Username,
|
||||
PermissionsJson = user.PermissionsJson
|
||||
Permissions = user.Permissions
|
||||
};
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ public class UsersController : Controller
|
||||
Email = request.Email,
|
||||
Username = request.Username,
|
||||
Password = hashedPassword,
|
||||
PermissionsJson = request.PermissionsJson
|
||||
Permissions = request.Permissions
|
||||
};
|
||||
|
||||
var finalUser = await UserRepository.Add(user);
|
||||
@@ -111,7 +111,7 @@ public class UsersController : Controller
|
||||
Id = finalUser.Id,
|
||||
Email = finalUser.Email,
|
||||
Username = finalUser.Username,
|
||||
PermissionsJson = finalUser.PermissionsJson
|
||||
Permissions = finalUser.Permissions
|
||||
};
|
||||
}
|
||||
|
||||
@@ -144,9 +144,9 @@ public class UsersController : Controller
|
||||
user.TokenValidTimestamp = DateTime.UtcNow; // Log out user after password change
|
||||
}
|
||||
|
||||
if (user.PermissionsJson != request.PermissionsJson)
|
||||
if (request.Permissions.Any(x => !user.Permissions.Contains(x)))
|
||||
{
|
||||
user.PermissionsJson = request.PermissionsJson;
|
||||
user.Permissions = request.Permissions;
|
||||
user.TokenValidTimestamp = DateTime.UtcNow; // Log out user after permission change
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ public class UsersController : Controller
|
||||
Id = user.Id,
|
||||
Email = user.Email,
|
||||
Username = user.Username,
|
||||
PermissionsJson = user.PermissionsJson
|
||||
Permissions = user.Permissions
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -73,9 +73,6 @@ public class AuthController : Controller
|
||||
if (user == null)
|
||||
throw new HttpApiException("Unable to load user data", 500);
|
||||
|
||||
//
|
||||
var permissions = JsonSerializer.Deserialize<string[]>(user.PermissionsJson) ?? [];
|
||||
|
||||
// Generate token
|
||||
var securityTokenDescriptor = new SecurityTokenDescriptor()
|
||||
{
|
||||
@@ -90,7 +87,7 @@ public class AuthController : Controller
|
||||
},
|
||||
{
|
||||
"permissions",
|
||||
string.Join(";", permissions)
|
||||
string.Join(";", user.Permissions)
|
||||
}
|
||||
},
|
||||
SigningCredentials = new SigningCredentials(
|
||||
@@ -122,13 +119,11 @@ public class AuthController : Controller
|
||||
var userId = int.Parse(userIdClaim.Value);
|
||||
var user = await UserRepository.Get().FirstAsync(x => x.Id == userId);
|
||||
|
||||
var permissions = JsonSerializer.Deserialize<string[]>(user.PermissionsJson) ?? [];
|
||||
|
||||
return new()
|
||||
{
|
||||
Email = user.Email,
|
||||
Username = user.Username,
|
||||
Permissions = string.Join(";", permissions)
|
||||
Permissions = user.Permissions
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
@using Moonlight.Shared.Misc
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="en" class="bg-background text-base-content font-inter">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
@@ -18,15 +18,15 @@
|
||||
<link rel="apple-touch-icon" sizes="192x192" href="/img/icon-192.png" />
|
||||
</head>
|
||||
|
||||
<body class="bg-gray-950 text-white font-inter h-full">
|
||||
<body>
|
||||
<div id="app">
|
||||
|
||||
<div class="flex h-screen justify-center items-center">
|
||||
<div class="sm:max-w-lg">
|
||||
<div id="blazor-loader-label" class="text-center mb-2 text-lg font-semibold"></div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="progress min-w-sm md:min-w-md" role="progressbar">
|
||||
<div id="blazor-loader-progress" class="progress-bar"></div>
|
||||
<div class="progress h-3 min-w-sm md:min-w-md" role="progressbar" aria-valuemin="0" aria-valuemax="100">
|
||||
<div id="blazor-loader-progress" class="progress-bar progress-primary"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -291,7 +291,7 @@ public partial class OAuth2Controller : Controller
|
||||
var userCount = await UserRepository.Get().CountAsync();
|
||||
|
||||
if (userCount == 0)
|
||||
user.PermissionsJson = "[\"*\"]";
|
||||
user.Permissions = ["*"];
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user