Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# This workflow will build a .NET project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net

name: .NET

on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Test
run: dotnet test --no-build --verbosity normal
8 changes: 4 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*.user
*.userosscache
*.sln.docstates
*.sln

# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
Expand All @@ -34,6 +35,7 @@ bld/

# Visual Studio 2015/2017 cache/options directory
.vs/

# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/

Expand Down Expand Up @@ -82,8 +84,6 @@ StyleCopReport.xml
*.pgc
*.pgd
*.rsp
# but not Directory.Build.rsp, as it configures directory-level build defaults
!Directory.Build.rsp
*.sbr
*.tlb
*.tli
Expand All @@ -95,7 +95,7 @@ StyleCopReport.xml
*.tlog
*.vspscc
*.vssscc
.builds
.build
*.pidb
*.svclog
*.scc
Expand Down Expand Up @@ -257,7 +257,6 @@ Generated_Code/
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
Expand Down Expand Up @@ -398,3 +397,4 @@ FodyWeavers.xsd

# JetBrains Rider
*.sln.iml
.idea/
3 changes: 2 additions & 1 deletion LICENSE.md
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
Parallel © 2025 by [Entex Interactive, LLC](https://www.entexinteractive.com/) is licensed under [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)
Parallel © 2025 by [Kyle Ebbinga](https://github.com/TheGuitarleader/Parallel) is licensed under [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/)

84 changes: 84 additions & 0 deletions Parallel.Cli/Commands/ConfigCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright 2025 Kyle Ebbinga

using System.CommandLine;
using Parallel.Cli.Utils;
using Parallel.Core.Database;
using Parallel.Core.IO.FileSystem;
using Parallel.Core.Settings;
using Parallel.Core.Utils;

namespace Parallel.Cli.Commands
{
public class ConfigCommand : Command
{
private Command addCmd = new("add", "Adds a new profile configuration.");
private Command editCmd = new("edit", "Edits a profile configuration.");
private Command viewCmd = new("view", "Shows the profile configuration.");
private Command setCmd = new("set", "Sets a new profile configuration.");
private Command delCmd = new("delete", "Deletes a profile configuration.");

public ConfigCommand() : base("config", "View or edit the profile configurations.")
{
this.SetHandler(() =>
{
//ProfileConfig profile = ProfileConfig.Load();
CommandLine.WriteLine($"Current profile: '{Program.Settings.Profiles.FirstOrDefault()}'");
});

this.AddCommand(addCmd);
addCmd.SetHandler(() =>
{
CommandLine.WriteLine("Creating new database credentials...", ConsoleColor.DarkGray);
DatabaseCredentials dbc = new DatabaseCredentials();
dbc.Provider = Enum.Parse<DatabaseProvider>(CommandLine.ReadString($"Provider ({string.Join(", ", Enum.GetNames(typeof(DatabaseProvider)))})"), true);
if (dbc.Provider == DatabaseProvider.Local)
{
dbc = DatabaseCredentials.Local;
}
else
{
dbc.Address = CommandLine.ReadString("Address");
dbc.Username = CommandLine.ReadString("Username");
dbc.Password = CommandLine.ReadPassword("Password");
dbc.Name = CommandLine.ReadString("Name");
}

CommandLine.WriteLine("Creating new file system credentials...", ConsoleColor.DarkGray);
FileSystemCredentials fsc = new FileSystemCredentials();
fsc.Service = Enum.Parse<FileService>(CommandLine.ReadString($"Service ({string.Join(", ", Enum.GetNames(typeof(FileService)))})"), true);
if (fsc.Service == FileService.Local)
{
fsc.RootDirectory = CommandLine.ReadString("Root");
}
else if (fsc.Service == FileService.Cloud)
{
fsc.Address = CommandLine.ReadString("Bucket Name");
fsc.Username = CommandLine.ReadString("Access Key");
fsc.Password = CommandLine.ReadPassword("Secret Key");
}
else
{
fsc.RootDirectory = CommandLine.ReadString("Root");
fsc.Address = CommandLine.ReadString("Address");
fsc.Username = CommandLine.ReadString("Username");
fsc.Password = CommandLine.ReadPassword("Password");
}

fsc.Encrypt = CommandLine.ReadBool("Encrypt files? (y/n)", false);
fsc.EncryptionKey = HashGenerator.GenerateHash(32, true);

string profileName = CommandLine.ReadString("Profile Name");
ProfileConfig profile = new ProfileConfig(profileName, dbc, fsc);
profile.SaveToFile();

CommandLine.WriteLine($"Saved new connection profile: '{profile.Name}'");
});

this.AddCommand(setCmd);
setCmd.SetHandler(() =>
{

});
}
}
}
14 changes: 14 additions & 0 deletions Parallel.Cli/Commands/DecryptCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2025 Kyle Ebbinga

using System.CommandLine;

namespace Parallel.Cli.Commands
{
public class DecryptCommand : Command
{
public DecryptCommand() : base("decrypt", "Decrypts a file or directory.")
{

}
}
}
16 changes: 16 additions & 0 deletions Parallel.Cli/Commands/EncryptCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2025 Kyle Ebbinga

using System.CommandLine;

namespace Parallel.Cli.Commands
{
public class EncryptCommand : Command
{
private readonly Argument<string> sourceArg = new("path", "The source path to encrypt.");

public EncryptCommand() : base("encrypt", "Encrypts a file or directory.")
{

}
}
}
78 changes: 78 additions & 0 deletions Parallel.Cli/Commands/UnzipCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright 2025 Kyle Ebbinga

using System.CommandLine;
using System.Diagnostics;
using System.IO.Compression;
using Parallel.Cli.Utils;

namespace Parallel.Cli.Commands
{
public class UnzipCommand : Command
{
private readonly Argument<string> sourceArg = new("path", "The source path of files to unzip.");
private readonly Option<bool> keepOpt = new(["--keep", "-k"], "If the original files should be kept.");

private Stopwatch _sw;
private readonly List<Task> _tasks = new List<Task>();
private int _totalTasks = 0;

public UnzipCommand() : base("unzip", "Unzips files in a directory.")
{
this.AddArgument(sourceArg);
this.AddOption(keepOpt);
this.SetHandler(async (path, keep) =>
{
_sw = Stopwatch.StartNew();
CommandLine.WriteLine($"Scanning for files in {path}...", ConsoleColor.DarkGray);
string[] files = Directory.GetFiles(path, $"*.gz", SearchOption.AllDirectories);
if (files.Length == 0)
{
CommandLine.WriteLine("No files found to unzip!", ConsoleColor.Yellow);
return;
}

CommandLine.WriteLine($"Unzipping {files.Length.ToString("N0")} files...", ConsoleColor.DarkGray);
_totalTasks = files.Length;
foreach (string file in files)
{
StartDecompressFile(file, keep);
}

await Task.WhenAll(_tasks);
CommandLine.WriteLine($"Successfully unzipped {files.Length.ToString("N0")} files in {_sw.Elapsed}.", ConsoleColor.Green);
}, sourceArg, keepOpt);
}

private void StartDecompressFile(string path, bool keep)
{
Task decompTask = Task.Run(() =>
{
DecompressFile(path, keep);
});

decompTask.ContinueWith(t => t.Dispose());
_tasks.Add(decompTask);
}

private void DecompressFile(string path, bool keep)
{
if (File.Exists(path))
{
using (FileStream openFile = File.OpenRead(path))
using (FileStream createFile = new FileStream(path.Replace(".gz", string.Empty), FileMode.OpenOrCreate))
using (GZipStream gZip = new GZipStream(openFile, CompressionMode.Decompress))
{
gZip.CopyTo(createFile);
}

if (!keep)
{
File.SetAttributes(path, ~FileAttributes.ReadOnly & File.GetAttributes(path));
File.Delete(path);
}
}

CommandLine.ProgressBar(_tasks.Count(t => t.IsCompleted), _totalTasks, _sw.Elapsed, ConsoleColor.DarkGray);
}
}
}
80 changes: 80 additions & 0 deletions Parallel.Cli/Commands/ZipCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright 2025 Kyle Ebbinga

using System.CommandLine;
using System.Diagnostics;
using System.IO.Compression;
using System.Runtime.CompilerServices;
using Parallel.Cli.Utils;
using SQLitePCL;

namespace Parallel.Cli.Commands
{
public class ZipCommand : Command
{
private readonly Argument<string> sourceArg = new("path", "The source path of files to zip.");
private readonly Option<bool> keepOpt = new(["--keep", "-k"], "If the original files should be kept.");

private Stopwatch _sw = new Stopwatch();
private readonly List<Task> _tasks = new List<Task>();
private int _totalTasks = 0;

public ZipCommand() : base("zip", "Zips files in a directory.")
{
this.AddArgument(sourceArg);
this.AddOption(keepOpt);
this.SetHandler(async (path, keep) =>
{
_sw = Stopwatch.StartNew();
CommandLine.WriteLine($"Scanning for files in {path}...", ConsoleColor.DarkGray);
string[] files = Directory.EnumerateFiles(path, $"*", SearchOption.AllDirectories).Where(f => !f.EndsWith(".gz")).ToArray();
if (files.Length == 0)
{
CommandLine.WriteLine("No files found to zip!", ConsoleColor.Yellow);
return;
}

CommandLine.WriteLine($"Zipping {files.Length.ToString("N0")} files...", ConsoleColor.DarkGray);
_totalTasks = files.Length;
foreach (string file in files)
{
StartCompressFile(file, keep);
}

await Task.WhenAll(_tasks);
CommandLine.WriteLine($"Successfully zipped {files.Length.ToString("N0")} files in {_sw.Elapsed}.", ConsoleColor.Green);
}, sourceArg, keepOpt);
}

private void StartCompressFile(string path, bool keep)
{
Task compTask = Task.Run(() =>
{
CompressFile(path, keep);
});

compTask.ContinueWith(t => t.Dispose());
_tasks.Add(compTask);
}

private void CompressFile(string path, bool keep)
{
if (File.Exists(path))
{
using (FileStream openFile = File.OpenRead(path))
using (FileStream createFile = new FileStream($"{path}.gz", FileMode.OpenOrCreate))
using (GZipStream gZip = new GZipStream(createFile, CompressionLevel.SmallestSize))
{
openFile.CopyTo(gZip);
}

if (!keep)
{
File.SetAttributes(path, ~FileAttributes.ReadOnly & File.GetAttributes(path));
File.Delete(path);
}
}

CommandLine.ProgressBar(_tasks.Count(t => t.IsCompleted), _totalTasks, _sw.Elapsed, ConsoleColor.DarkGray);
}
}
}
Loading