瀏覽代碼

Merge pull request #42 from Moonlight-Panel/FtpFileManager

Ftp file manager
Daniel Balk 2 年之前
父節點
當前提交
1b7988547c

+ 184 - 0
Moonlight/App/Helpers/Files/FtpFileAccess.cs

@@ -0,0 +1,184 @@
+using System.Net;
+using System.Text;
+using FluentFTP;
+
+namespace Moonlight.App.Helpers.Files;
+
+public class FtpFileAccess : FileAccess
+{
+    private string FtpHost, FtpUser, FtpPassword;
+    private int FtpPort;
+
+    private AsyncFtpClient Client;
+    
+    public FtpFileAccess(string ftpHost, int ftpPort, string ftpUser, string ftpPassword)
+    {
+        FtpHost = ftpHost;
+        FtpPort = ftpPort;
+        FtpUser = ftpUser;
+        FtpPassword = ftpPassword;
+
+        Client = new AsyncFtpClient(FtpHost, FtpUser, FtpPassword, FtpPort);
+    }
+
+    private async Task EnsureConnect()
+    {
+        if (!Client.IsConnected)
+            await Client.AutoConnect();
+    }
+    
+    public override async Task<FileData[]> Ls()
+    {
+        await EnsureConnect();
+        
+        var x = new List<FileData>();
+        
+        foreach (FtpListItem item in (await Client.GetListing(CurrentPath)).OrderBy(x => x.Type + " " + x.Name))
+        {
+            long size = 0;
+            
+            if (item.Type == FtpObjectType.File)
+            {
+                size = await Client.GetFileSize(item.FullName);
+            }
+
+            x.Add(new()
+            {
+                Name = item.Name,
+                Size = size,
+                IsFile = item.Type == FtpObjectType.File,
+            });
+        }
+
+        return x.ToArray();
+    }
+
+    public override Task Cd(string dir)
+    {
+        var x = Path.Combine(CurrentPath, dir).Replace("\\", "/") + "/";
+        x = x.Replace("//", "/");
+        CurrentPath = x;
+
+        return Task.CompletedTask;
+    }
+
+    public override Task Up()
+    {
+        CurrentPath = Path.GetFullPath(Path.Combine(CurrentPath, "..")).Replace("\\", "/").Replace("C:", "");
+        return Task.CompletedTask;
+    }
+
+    public override Task SetDir(string dir)
+    {
+        CurrentPath = dir;
+        return Task.CompletedTask;
+    }
+
+    public override async Task<string> Read(FileData fileData)
+    {
+        await EnsureConnect();
+
+        var s = new MemoryStream();
+        await Client.DownloadStream(s, CurrentPath.TrimEnd('/') + "/" + fileData.Name);
+        var data = s.ToArray();
+        s.Dispose();
+        var str = Encoding.UTF8.GetString(data);
+        return str;
+    }
+
+    public override async Task Write(FileData fileData, string content)
+    {
+        await EnsureConnect();
+
+        var s = new MemoryStream();
+        s.Write(Encoding.UTF8.GetBytes(content));
+        s.Position = 0;
+        await Client.UploadStream(s, CurrentPath.TrimEnd('/') + "/" + fileData.Name, FtpRemoteExists.Overwrite);
+        s.Dispose();
+    }
+
+    public override async Task Upload(string name, Stream dataStream, Action<int>? progressUpdated = null)
+    {
+        await EnsureConnect();
+
+        IProgress<FtpProgress> progress = new Progress<FtpProgress>(x =>
+        {
+            progressUpdated((int) x.Progress);
+        });
+        await Client.UploadStream(dataStream, CurrentPath.TrimEnd('/') + "/" + name, FtpRemoteExists.Overwrite, false, progress);
+        dataStream.Dispose();
+    }
+
+    public override async Task MkDir(string name)
+    {
+        await EnsureConnect();
+
+        await Client.CreateDirectory(CurrentPath.TrimEnd('/') + "/" + name + "/");
+    }
+
+    public override Task<string> Pwd()
+    {
+        return Task.FromResult(CurrentPath);
+    }
+
+    public override async Task<string> DownloadUrl(FileData fileData)
+    {
+        await EnsureConnect();
+
+        throw new NotImplementedException();
+    }
+
+    public override async Task<Stream> DownloadStream(FileData fileData)
+    {
+        await EnsureConnect();
+
+        var s = new MemoryStream();
+        await Client.DownloadStream(s, CurrentPath.TrimEnd('/') + "/" + fileData.Name);
+        return s;
+    }
+
+    public override async Task Delete(FileData fileData)
+    {
+        await EnsureConnect();
+
+        if (fileData.IsFile)
+            await Client.DeleteFile(CurrentPath.TrimEnd('/') + "/" + fileData.Name);
+        else
+            await Client.DeleteDirectory(CurrentPath.TrimEnd('/') + "/" + fileData.Name);
+    }
+
+    public override async Task Move(FileData fileData, string newPath)
+    {
+        await EnsureConnect();
+
+        if (fileData.IsFile)
+            await Client.MoveFile(CurrentPath.TrimEnd('/') + "/" + fileData.Name, newPath);
+        else
+            await Client.MoveDirectory(CurrentPath.TrimEnd('/') + "/" + fileData.Name, newPath);
+    }
+
+    public override async Task Compress(params FileData[] files)
+    {
+        await EnsureConnect();
+
+        throw new NotImplementedException();
+    }
+
+    public override async Task Decompress(FileData fileData)
+    {
+        await EnsureConnect();
+
+        throw new NotImplementedException();
+    }
+
+    public override Task<string> GetLaunchUrl()
+    {
+        return Task.FromResult(
+                    $"ftp://{FtpUser}:{FtpPassword}@{FtpHost}:{FtpPort}/");
+    }
+
+    public override object Clone()
+    {
+        return new FtpFileAccess(FtpHost, FtpPort, FtpUser, FtpPassword);
+    }
+}

+ 1 - 0
Moonlight/Moonlight.csproj

@@ -20,6 +20,7 @@
     <PackageReference Include="CloudFlare.Client" Version="6.1.4" />
     <PackageReference Include="CurrieTechnologies.Razor.SweetAlert2" Version="5.4.0" />
     <PackageReference Include="Discord.Net" Version="3.9.0" />
+    <PackageReference Include="FluentFTP" Version="46.0.2" />
     <PackageReference Include="GravatarSharp.Core" Version="1.0.1.2" />
     <PackageReference Include="JWT" Version="10.0.2" />
     <PackageReference Include="Logging.Net" Version="1.1.0" />

+ 3 - 3
Moonlight/Shared/Components/FileManagerPartials/FileManager.razor

@@ -24,7 +24,7 @@
 else
 {
     <div class="card mb-7">
-        <div class="card-header">
+        <div class="card-header border-0 my-2">
             <div class="card-title">
                 <div class="d-flex flex-stack">
                     <FilePath Access="Access" OnPathChanged="OnComponentStateChanged" />
@@ -59,7 +59,7 @@ else
                     else
                     {
                         <button type="button" @onclick="Launch" class="btn btn-light-primary me-3">
-                            <span class="svg-icon svg-icon-muted svg-icon-2hx">
+                            <span class="svg-icon svg-icon-muted svg-icon-2">
                                 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
                                     <path opacity="0.3" d="M5 16C3.3 16 2 14.7 2 13C2 11.3 3.3 10 5 10H5.1C5 9.7 5 9.3 5 9C5 6.2 7.2 4 10 4C11.9 4 13.5 5 14.3 6.5C14.8 6.2 15.4 6 16 6C17.7 6 19 7.3 19 9C19 9.4 18.9 9.7 18.8 10C18.9 10 18.9 10 19 10C20.7 10 22 11.3 22 13C22 14.7 20.7 16 19 16H5ZM8 13.6H16L12.7 10.3C12.3 9.89999 11.7 9.89999 11.3 10.3L8 13.6Z" fill="currentColor"/>
                                     <path d="M11 13.6V19C11 19.6 11.4 20 12 20C12.6 20 13 19.6 13 19V13.6H11Z" fill="currentColor"/>
@@ -86,7 +86,7 @@ else
         </div>
     </div>
 
-    <div class="card card-body">
+    <div class="card card-body ps-9">
         <FileView @ref="View"
                   Access="Access"
                   ContextActions="Actions"

+ 1 - 1
Moonlight/Shared/Components/FileManagerPartials/FileView.razor

@@ -97,7 +97,7 @@
                             <td>@(Formatter.FormatSize(file.Size))</td>
                             <td class="text-end">
                                 <div class="d-flex justify-content-end">
-                                    <div class="ms-2 me-7">
+                                    <div class="ms-2 me-6">
                                         @if (ContextActions.Any())
                                         {
                                             <ContextMenuTrigger MenuId="triggerMenu" MouseButtonTrigger="MouseButtonTrigger.Both" Data="file">

+ 1 - 6
Moonlight/Shared/Views/Test.razor

@@ -27,12 +27,7 @@
 
     private Task Load(LazyLoader arg)
     {
-        var server = ServerRepository
-            .Get()
-            .Include(x => x.Node)
-            .First();
-
-        FileAccess = new WingsFileAccess(WingsApiHelper, WingsJwtHelper, server, ConfigService, User);
+        FileAccess = new FtpFileAccess("vps01.so.host.endelon.link", 21, "example.com", "61P8JZzfjSNyhtZl");
 
         return Task.CompletedTask;
     }

+ 5 - 0
Moonlight/resources/lang/de_de.lang

@@ -482,3 +482,8 @@ Node offline;Node offline
 The node the server is running on is currently offline;The node the server is running on is currently offline
 Server not found;Server not found
 A server with that id cannot be found or you have no access for this server;A server with that id cannot be found or you have no access for this server
+Compress;Compress
+Decompress;Decompress
+Moving;Moving
+Compressing;Compressing
+selected;selected