+
@@ -99,7 +100,7 @@
if (!SelectAll)
{
- // filter the providers which have been selected if not all providers have been selected
+ // Filter the providers which have been selected if not all providers have been selected
request.Providers = AvailableProviders
.Where(x => x.Value)
.Select(x => x.Key.Type)
@@ -108,7 +109,7 @@
var stream = await ApiClient.PostStream("api/admin/system/diagnose", request);
- await DownloadService.DownloadStream("diagnose.zip", stream);
+ await DownloadService.Download("diagnose.zip", stream);
}
diff --git a/Moonlight.Client/UI/Views/Admin/Sys/Files.razor b/Moonlight.Client/UI/Views/Admin/Sys/Files.razor
index 376f075b..cce5691a 100644
--- a/Moonlight.Client/UI/Views/Admin/Sys/Files.razor
+++ b/Moonlight.Client/UI/Views/Admin/Sys/Files.razor
@@ -3,31 +3,28 @@
@using Microsoft.AspNetCore.Authorization
@using MoonCore.Blazor.Services
@using MoonCore.Helpers
-@using MoonCore.Blazor.Tailwind.Fm
@using Moonlight.Client.Implementations
+@using MoonCore.Blazor.FlyonUi.Files.Manager
@attribute [Authorize(Policy = "permissions:admin.system.overview")]
@inject HttpApiClient ApiClient
-@inject DownloadService DownloadService
-@inject LocalStorageService LocalStorageService
-
+
@code
{
- private IFileSystemProvider FileSystemProvider;
+ private IFsAccess FsAccess;
+ private static readonly long TransferChunkSize = ByteConverter.FromMegaBytes(20).Bytes;
+ private static readonly long UploadLimit = ByteConverter.FromGigaBytes(20).Bytes;
+
protected override void OnInitialized()
{
- FileSystemProvider = new SysFileSystemProvider(
- ApiClient,
- DownloadService,
- LocalStorageService
- );
+ FsAccess = new SystemFsAccess(ApiClient);
}
}
diff --git a/Moonlight.Client/UI/Views/Admin/Sys/Index.razor b/Moonlight.Client/UI/Views/Admin/Sys/Index.razor
index 7f32a7b8..4ed5763c 100644
--- a/Moonlight.Client/UI/Views/Admin/Sys/Index.razor
+++ b/Moonlight.Client/UI/Views/Admin/Sys/Index.razor
@@ -22,8 +22,8 @@
-
-
+
+
Restart/Shutdown
diff --git a/Moonlight.Client/UI/Views/Admin/Users/Create.razor b/Moonlight.Client/UI/Views/Admin/Users/Create.razor
index 69fbaa12..bf741df6 100644
--- a/Moonlight.Client/UI/Views/Admin/Users/Create.razor
+++ b/Moonlight.Client/UI/Views/Admin/Users/Create.razor
@@ -3,6 +3,7 @@
@using System.Text.Json
@using MoonCore.Helpers
@using Moonlight.Shared.Http.Requests.Admin.Users
+@using MoonCore.Blazor.FlyonUi.Forms
@inject HttpApiClient ApiClient
@inject NavigationManager Navigation
@@ -10,11 +11,11 @@
-
+
Back
-
+
Create
@@ -23,27 +24,27 @@
-
+
-
+
@@ -55,7 +56,7 @@
private HandleForm Form;
private CreateUserRequest Request;
- private string[] Permissions = [];
+ private List Permissions = [];
protected override void OnInitialized()
{
@@ -64,7 +65,7 @@
private async Task OnSubmit()
{
- Request.PermissionsJson = JsonSerializer.Serialize(Permissions);
+ Request.Permissions = Permissions.ToArray();
await ApiClient.Post("api/admin/users", Request);
diff --git a/Moonlight.Client/UI/Views/Admin/Users/Index.razor b/Moonlight.Client/UI/Views/Admin/Users/Index.razor
index f0a89872..fd5b79f9 100644
--- a/Moonlight.Client/UI/Views/Admin/Users/Index.razor
+++ b/Moonlight.Client/UI/Views/Admin/Users/Index.razor
@@ -32,8 +32,8 @@
Delete(context)" @onclick:preventDefault
- class="text-danger">
-
+ class="text-error">
+
diff --git a/Moonlight.Client/UI/Views/Admin/Users/Update.razor b/Moonlight.Client/UI/Views/Admin/Users/Update.razor
index fd48300a..eab94872 100644
--- a/Moonlight.Client/UI/Views/Admin/Users/Update.razor
+++ b/Moonlight.Client/UI/Views/Admin/Users/Update.razor
@@ -4,6 +4,7 @@
@using MoonCore.Helpers
@using Moonlight.Shared.Http.Requests.Admin.Users
@using Moonlight.Shared.Http.Responses.Admin.Users
+@using MoonCore.Blazor.FlyonUi.Forms
@inject HttpApiClient ApiClient
@inject NavigationManager Navigation
@@ -12,11 +13,11 @@
-
+
Back
-
+
Update
@@ -25,27 +26,27 @@
-
+
-
+
@@ -60,25 +61,25 @@
private HandleForm Form;
private UpdateUserRequest Request;
- private string[] Permissions = [];
+ private List Permissions = [];
private async Task Load(LazyLoader _)
{
var detail = await ApiClient.GetJson($"api/admin/users/{Id}");
- Permissions = JsonSerializer.Deserialize(detail.PermissionsJson) ?? [];
+ Permissions = detail.Permissions.ToList();
Request = new()
{
Email = detail.Email,
- PermissionsJson = detail.PermissionsJson,
+ Permissions = detail.Permissions,
Username = detail.Username
};
}
private async Task OnSubmit()
{
- Request.PermissionsJson = JsonSerializer.Serialize(Permissions);
+ Request.Permissions = Permissions.ToArray();
await ApiClient.Patch($"api/admin/users/{Id}", Request);
diff --git a/Moonlight.Client/wwwroot/js/moonCore.js b/Moonlight.Client/wwwroot/js/moonCore.js
deleted file mode 100644
index 451773a6..00000000
--- a/Moonlight.Client/wwwroot/js/moonCore.js
+++ /dev/null
@@ -1,316 +0,0 @@
-window.moonCore = {
- window: {
- getSize: function () {
- return [window.innerWidth, window.innerHeight];
- }
- },
- keyBinds: {
- storage: {},
-
- registerHotkey: function (key, modifier, action, dotNetObjRef) {
-
- const hotkeyListener = async (event) => {
- if (event.code === key && (!modifier || event[modifier + 'Key'])) {
- event.preventDefault();
-
- await dotNetObjRef.invokeMethodAsync("OnHotkeyPressed", action);
- }
- };
-
- moonCore.keyBinds.storage[`${key}${modifier}`] = hotkeyListener;
- window.addEventListener('keydown', hotkeyListener);
- },
-
- unregisterHotkey: function (key, modifier) {
- const listenerKey = `${key}${modifier}`;
- if (moonCore.keyBinds.storage[listenerKey]) {
- window.removeEventListener('keydown', moonCore.keyBinds.storage[listenerKey]);
- delete moonCore.keyBinds.storage[listenerKey];
- }
- }
- },
- downloadService: {
- download: async function (fileName, contentStreamReference, id, reportRef) {
- const promise = new Promise(async resolve => {
- const stream = await contentStreamReference.stream();
- const reader = stream.getReader();
-
- let lastReportTime = 0;
- let receivedLength = 0; // Track downloaded size
- let chunks = []; // Store downloaded chunks
-
- while (true) {
- const {done, value} = await reader.read();
- if (done) break;
-
- chunks.push(value);
- receivedLength += value.length;
-
- if (reportRef) {
- const now = Date.now();
-
- if (now - lastReportTime >= 500) { // Only log once per second
- await reportRef.invokeMethodAsync("ReceiveReport", id, receivedLength, -1, false);
- lastReportTime = now;
- }
- }
- }
-
- // Combine chunks into a single Blob
- const blob = new Blob(chunks);
-
- this.downloadBlob(fileName, blob);
-
- if (reportRef)
- await reportRef.invokeMethodAsync("ReceiveReport", id, receivedLength, -1, true);
-
- resolve();
- });
-
- await promise;
- },
- downloadUrl: async function (fileName, url, reportRef, id, headers) {
- const promise = new Promise(async resolve => {
- let loadRequest = new XMLHttpRequest();
- let lastReported = Date.now();
-
- loadRequest.open("GET", url, true);
-
- for(let headerKey in headers) {
- loadRequest.setRequestHeader(headerKey, headers[headerKey]);
- }
-
- loadRequest.responseType = "blob";
-
- if (reportRef) {
- loadRequest.onprogress = async ev => {
- const now = Date.now();
-
- if (now - lastReported >= 500) {
- await reportRef.invokeMethodAsync("ReceiveReport", id, ev.loaded, ev.total, false);
- lastReported = now;
- }
- };
-
- loadRequest.onloadend = async ev => {
- await reportRef.invokeMethodAsync("ReceiveReport", id, ev.loaded, ev.total, true);
- }
- }
-
- loadRequest.onload = _ => {
- this.downloadBlob(fileName, loadRequest.response);
-
- resolve();
- }
-
- loadRequest.send();
- });
-
- await promise;
- },
- downloadBlob: function (fileName, blob)
- {
- const url = URL.createObjectURL(blob);
-
- // Trigger file download
- const anchor = document.createElement("a");
- anchor.href = url;
- anchor.download = fileName;
- document.body.appendChild(anchor);
- anchor.click();
-
- document.body.removeChild(anchor);
- URL.revokeObjectURL(url);
- }
- },
- fileManager: {
- uploadCache: [],
- addFilesToCache: async function(id) {
- let files = document.getElementById(id).files;
-
-
- for (let i = 0; i < files.length; i++) {
- moonCore.fileManager.uploadCache.push(files[i]);
- }
-
- await this.ref.invokeMethodAsync("TriggerUpload", moonCore.fileManager.uploadCache.length);
- },
- getNextFromCache: async function() {
- if(moonCore.fileManager.uploadCache.length === 0)
- return null;
-
- let nextItem = moonCore.fileManager.uploadCache.pop();
-
- if(!nextItem)
- return null;
-
- let file;
- let path;
-
- if(nextItem instanceof File)
- {
- file = nextItem;
- path = file.name;
- }
- else
- {
- file = await this.openFileEntry(nextItem);
- path = nextItem.fullPath;
- }
-
- if(file.size === 0)
- {
- return {
- path: null,
- stream: null,
- left: moonCore.fileManager.uploadCache.length
- }
- }
-
- let stream = await this.createStreamRef(file);
-
- return {
- path: path,
- stream: stream,
- left: moonCore.fileManager.uploadCache.length
- };
- },
- openFileEntry: async function (fileEntry) {
- const promise = new Promise(resolve => {
- fileEntry.file(file => {
- resolve(file);
- }, err => console.log(err));
- });
-
- return await promise;
- },
- createStreamRef: async function (processedFile) {
- // Prevent uploads of empty files
- if (processedFile.size <= 0) {
- console.log("Skipping upload of '" + processedFile.name + "' as its empty");
- return null;
- }
-
- const fileReader = new FileReader();
-
- const readerPromise = new Promise(resolve => {
- fileReader.addEventListener("loadend", ev => {
- resolve(fileReader.result)
- });
- });
-
- fileReader.readAsArrayBuffer(processedFile);
-
- const arrayBuffer = await readerPromise;
-
- return DotNet.createJSStreamReference(arrayBuffer);
- },
- setup: function (id, callbackRef) {
- this.ref = callbackRef;
-
- // Check which features are supported by the browser
- const supportsFileSystemAccessAPI =
- 'getAsFileSystemHandle' in DataTransferItem.prototype;
- const supportsWebkitGetAsEntry =
- 'webkitGetAsEntry' in DataTransferItem.prototype;
-
- // This is the drag and drop zone.
- const elem = document.getElementById(id);
-
- // Prevent navigation.
- elem.addEventListener('dragover', (e) => {
- e.preventDefault();
- });
-
- elem.addEventListener('drop', async (e) => {
- // Prevent navigation.
- e.preventDefault();
-
- if (!supportsFileSystemAccessAPI && !supportsWebkitGetAsEntry) {
- // Cannot handle directories.
- console.log("Cannot handle directories");
- return;
- }
-
- this.getAllWebkitFileEntries(e.dataTransfer.items).then(async value => {
- value.forEach(a => moonCore.fileManager.uploadCache.push(a));
- await this.ref.invokeMethodAsync("TriggerUpload", this.uploadCache.length);
- });
- });
- },
- getAllWebkitFileEntries: async function (dataTransferItemList) {
- function readAllEntries(reader) {
- return new Promise((resolve, reject) => {
- const entries = [];
- function readEntries() {
- reader.readEntries((batch) => {
- if (batch.length === 0) {
- resolve(entries);
- } else {
- entries.push(...batch);
- readEntries();
- }
- }, reject);
- }
- readEntries();
- });
- }
-
- async function traverseEntry(entry) {
- if (entry.isFile) {
- return [entry];
- } else if (entry.isDirectory) {
- const reader = entry.createReader();
- const entries = await readAllEntries(reader);
- const subEntries = await Promise.all(entries.map(traverseEntry));
- return subEntries.flat();
- }
- return [];
- }
-
- const entries = [];
-
- // Convert DataTransferItemList to entries
- for (let i = 0; i < dataTransferItemList.length; i++) {
- const item = dataTransferItemList[i];
- const entry = item.webkitGetAsEntry();
- if (entry) {
- entries.push(entry);
- }
- }
-
- // Traverse all entries and collect file entries
- const allFileEntries = await Promise.all(entries.map(traverseEntry));
- return allFileEntries.flat();
- }
- },
- codeEditor: {
- instances: new Map(),
- attach: function (id, options) {
- const editor = ace.edit(id, options);
-
- moonCore.codeEditor.instances.set(id, editor);
- },
- updateOptions: function (id, options) {
- const editor = moonCore.codeEditor.instances.get(id);
-
- editor.setOptions(options);
- },
- getValue: function (id) {
- const editor = moonCore.codeEditor.instances.get(id);
-
- return editor.getValue();
- },
- destroy: function (id){
- const editor = moonCore.codeEditor.instances.get(id);
-
- if(!editor)
- return;
-
- editor.destroy();
- editor.container.remove();
-
- moonCore.codeEditor.instances.delete(id);
- }
- }
-}
\ No newline at end of file
diff --git a/Moonlight.Shared/Http/Requests/Admin/ApiKeys/CreateApiKeyRequest.cs b/Moonlight.Shared/Http/Requests/Admin/ApiKeys/CreateApiKeyRequest.cs
index 28c65e0c..83d76bf6 100644
--- a/Moonlight.Shared/Http/Requests/Admin/ApiKeys/CreateApiKeyRequest.cs
+++ b/Moonlight.Shared/Http/Requests/Admin/ApiKeys/CreateApiKeyRequest.cs
@@ -8,7 +8,7 @@ public class CreateApiKeyRequest
public string Description { get; set; }
[Required(ErrorMessage = "You need to specify permissions for the api key")]
- public string PermissionsJson { get; set; } = "[]";
+ public string[] Permissions { get; set; } = [];
[Required(ErrorMessage = "You need to specify an expire date")]
public DateTime ExpiresAt { get; set; } = DateTime.UtcNow.AddDays(30);
diff --git a/Moonlight.Shared/Http/Requests/Admin/Users/CreateUserRequest.cs b/Moonlight.Shared/Http/Requests/Admin/Users/CreateUserRequest.cs
index b50ad161..b67361de 100644
--- a/Moonlight.Shared/Http/Requests/Admin/Users/CreateUserRequest.cs
+++ b/Moonlight.Shared/Http/Requests/Admin/Users/CreateUserRequest.cs
@@ -17,5 +17,5 @@ public class CreateUserRequest
[MaxLength(256, ErrorMessage = "Your password should not exceed the length of 256 characters")]
public string Password { get; set; }
- public string PermissionsJson { get; set; } = "[]";
+ public string[] Permissions { get; set; } = [];
}
\ No newline at end of file
diff --git a/Moonlight.Shared/Http/Requests/Admin/Users/UpdateUserRequest.cs b/Moonlight.Shared/Http/Requests/Admin/Users/UpdateUserRequest.cs
index 99b506e9..81362d31 100644
--- a/Moonlight.Shared/Http/Requests/Admin/Users/UpdateUserRequest.cs
+++ b/Moonlight.Shared/Http/Requests/Admin/Users/UpdateUserRequest.cs
@@ -13,7 +13,7 @@ public class UpdateUserRequest
public string Username { get; set; }
public string? Password { get; set; }
-
+
[Required(ErrorMessage = "You need to provide permissions")]
- public string PermissionsJson { get; set; } = "[]";
+ public string[] Permissions { get; set; } = [];
}
\ No newline at end of file
diff --git a/Moonlight.Shared/Http/Responses/Admin/ApiKeys/ApiKeyResponse.cs b/Moonlight.Shared/Http/Responses/Admin/ApiKeys/ApiKeyResponse.cs
index 826cac2b..28fe84a5 100644
--- a/Moonlight.Shared/Http/Responses/Admin/ApiKeys/ApiKeyResponse.cs
+++ b/Moonlight.Shared/Http/Responses/Admin/ApiKeys/ApiKeyResponse.cs
@@ -4,6 +4,6 @@ public class ApiKeyResponse
{
public int Id { get; set; }
public string Description { get; set; }
- public string PermissionsJson { get; set; } = "[]";
- public DateTime ExpiresAt { get; set; }
+ public string[] Permissions { get; set; } = [];
+ public DateTimeOffset ExpiresAt { get; set; }
}
\ No newline at end of file
diff --git a/Moonlight.Shared/Http/Responses/Admin/ApiKeys/CreateApiKeyResponse.cs b/Moonlight.Shared/Http/Responses/Admin/ApiKeys/CreateApiKeyResponse.cs
index f8c865ff..69c2555c 100644
--- a/Moonlight.Shared/Http/Responses/Admin/ApiKeys/CreateApiKeyResponse.cs
+++ b/Moonlight.Shared/Http/Responses/Admin/ApiKeys/CreateApiKeyResponse.cs
@@ -5,6 +5,6 @@ public class CreateApiKeyResponse
public int Id { get; set; }
public string Secret { get; set; }
public string Description { get; set; }
- public string PermissionsJson { get; set; } = "[]";
- public DateTime ExpiresAt { get; set; }
+ public string[] Permissions { get; set; } = [];
+ public DateTimeOffset ExpiresAt { get; set; }
}
\ No newline at end of file
diff --git a/Moonlight.Shared/Http/Responses/Admin/Sys/FileSystemEntryResponse.cs b/Moonlight.Shared/Http/Responses/Admin/Sys/FileSystemEntryResponse.cs
index a665a15f..f7bb124a 100644
--- a/Moonlight.Shared/Http/Responses/Admin/Sys/FileSystemEntryResponse.cs
+++ b/Moonlight.Shared/Http/Responses/Admin/Sys/FileSystemEntryResponse.cs
@@ -3,7 +3,7 @@
public class FileSystemEntryResponse
{
public string Name { get; set; }
- public bool IsFile { get; set; }
+ public bool IsFolder { get; set; }
public long Size { get; set; }
public DateTime CreatedAt { get; set; }
diff --git a/Moonlight.Shared/Http/Responses/Admin/Users/UserResponse.cs b/Moonlight.Shared/Http/Responses/Admin/Users/UserResponse.cs
index 60b0964e..23f50047 100644
--- a/Moonlight.Shared/Http/Responses/Admin/Users/UserResponse.cs
+++ b/Moonlight.Shared/Http/Responses/Admin/Users/UserResponse.cs
@@ -5,5 +5,5 @@ public class UserResponse
public int Id { get; set; }
public string Username { get; set; }
public string Email { get; set; }
- public string PermissionsJson { get; set; }
+ public string[] Permissions { get; set; }
}
\ No newline at end of file
diff --git a/Moonlight.Shared/Http/Responses/Auth/CheckResponse.cs b/Moonlight.Shared/Http/Responses/Auth/CheckResponse.cs
index 29511b4c..4073f3df 100644
--- a/Moonlight.Shared/Http/Responses/Auth/CheckResponse.cs
+++ b/Moonlight.Shared/Http/Responses/Auth/CheckResponse.cs
@@ -4,5 +4,5 @@ public class CheckResponse
{
public string Username { get; set; }
public string Email { get; set; }
- public string Permissions { get; set; }
+ public string[] Permissions { get; set; }
}
\ No newline at end of file