Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
111 changes: 82 additions & 29 deletions src/Neo.CLI/CLI/MainService.Plugins.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,27 +83,56 @@ private static async Task<Stream> DownloadPluginAsync(string pluginName, Version
var asmName = Assembly.GetExecutingAssembly().GetName();
httpClient.DefaultRequestHeaders.UserAgent.Add(new(asmName.Name!, asmName.Version!.ToString(3)));

var json = await httpClient.GetFromJsonAsync<JsonArray>(Settings.Default.Plugins.DownloadUrl) ?? throw new HttpRequestException($"Failed: {Settings.Default.Plugins.DownloadUrl}");
var jsonRelease = json.AsArray()
.SingleOrDefault(s =>
s != null &&
s["tag_name"]!.GetValue<string>() == $"v{pluginVersion.ToString(3)}" &&
s["prerelease"]!.GetValue<bool>() == prerelease) ?? throw new Exception($"Could not find Release {pluginVersion}");

var jsonAssets = jsonRelease
.AsObject()
.SingleOrDefault(s => s.Key == "assets").Value ?? throw new Exception("Could not find any Plugins");

var jsonPlugin = jsonAssets
.AsArray()
.SingleOrDefault(s =>
Path.GetFileNameWithoutExtension(
s!["name"]!.GetValue<string>()).Equals(pluginName, StringComparison.InvariantCultureIgnoreCase))
?? throw new Exception($"Could not find {pluginName}");

var downloadUrl = jsonPlugin["browser_download_url"]!.GetValue<string>();

return await httpClient.GetStreamAsync(downloadUrl);
var urls = new List<Uri> { Settings.Default.Plugins.DownloadUrl };
urls.AddRange(Settings.Default.Plugins.CustomUrls);

foreach (var url in urls)
{
try
{
var json = await httpClient.GetFromJsonAsync<JsonArray>(url);
if (json == null)
{
ConsoleHelper.Warning($"Failed to retrieve plugins from {url}");
continue;
}

var jsonRelease = json.AsArray()
.SingleOrDefault(s =>
s != null &&
s["tag_name"]!.GetValue<string>() == $"v{pluginVersion.ToString(3)}" &&
s["prerelease"]!.GetValue<bool>() == prerelease);

if (jsonRelease != null)
{
var jsonAssets = jsonRelease
.AsObject()
.SingleOrDefault(s => s.Key == "assets").Value ?? throw new Exception("Could not find any Plugins");

var jsonPlugin = jsonAssets
.AsArray()
.SingleOrDefault(s =>
Path.GetFileNameWithoutExtension(
s!["name"]!.GetValue<string>()).Equals(pluginName, StringComparison.InvariantCultureIgnoreCase))
?? throw new Exception($"Could not find {pluginName}");

var downloadUrl = jsonPlugin["browser_download_url"]!.GetValue<string>();

return await httpClient.GetStreamAsync(downloadUrl);
}

if (url == Settings.Default.Plugins.DownloadUrl)
{
ConsoleHelper.Warning($"Plugin not found in default URL: {url}");
}
}
catch (Exception ex)
{
ConsoleHelper.Warning($"Failed to retrieve plugins from {url}. Error: {ex.Message}");
}
}

throw new Exception($"Could not find {pluginName} in any provided URLs.");
}

/// <summary>
Expand All @@ -123,7 +152,6 @@ private async Task<bool> InstallPluginAsync(

try
{

using var stream = await DownloadPluginAsync(pluginName, Settings.Default.Plugins.Version, Settings.Default.Plugins.Prerelease);

using var zip = new ZipArchive(stream, ZipArchiveMode.Read);
Expand Down Expand Up @@ -264,13 +292,38 @@ private async Task<IEnumerable<string>> GetPluginListAsync()
var asmName = Assembly.GetExecutingAssembly().GetName();
httpClient.DefaultRequestHeaders.UserAgent.Add(new(asmName.Name!, asmName.Version!.ToString(3)));

var json = await httpClient.GetFromJsonAsync<JsonArray>(Settings.Default.Plugins.DownloadUrl) ?? throw new HttpRequestException($"Failed: {Settings.Default.Plugins.DownloadUrl}");
return json.AsArray()
.Where(w =>
w != null &&
w["tag_name"]!.GetValue<string>() == $"v{Settings.Default.Plugins.Version.ToString(3)}")
.SelectMany(s => s!["assets"]!.AsArray())
.Select(s => Path.GetFileNameWithoutExtension(s!["name"]!.GetValue<string>()));
var urls = new List<Uri> { Settings.Default.Plugins.DownloadUrl };
urls.AddRange(Settings.Default.Plugins.CustomUrls);

var pluginNames = new HashSet<string>();

foreach (var url in urls)
{
try
{
var json = await httpClient.GetFromJsonAsync<JsonArray>(url);
if (json == null)
{
ConsoleHelper.Warning($"Failed to retrieve plugins from {url}");
continue;
}

var plugins = json.AsArray()
.Where(w =>
w != null &&
w["tag_name"]!.GetValue<string>() == $"v{Settings.Default.Plugins.Version.ToString(3)}")
.SelectMany(s => s!["assets"]!.AsArray())
.Select(s => Path.GetFileNameWithoutExtension(s!["name"]!.GetValue<string>()));

pluginNames.UnionWith(plugins);
}
catch (Exception ex)
{
ConsoleHelper.Warning($"Failed to retrieve plugins from {url}. Error: {ex.Message}");
}
}

return pluginNames;
}
}
}
5 changes: 4 additions & 1 deletion src/Neo.CLI/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using Neo.Network.P2P;
using Neo.Persistence;
using System;
using System.Linq;
using System.Reflection;
using System.Threading;

Expand Down Expand Up @@ -165,7 +166,8 @@ public ContractsSettings() { }

public class PluginsSettings
{
public Uri DownloadUrl { get; init; } = new("https://api.github.com/repos/neo-project/neo-modules/releases");
public Uri DownloadUrl { get; init; } = new("https://api.github.com/repos/neo-project/neo/releases");
public Uri[] CustomUrls { get; init; } = [];
public bool Prerelease { get; init; } = false;
public Version Version { get; init; } = Assembly.GetExecutingAssembly().GetName().Version!;

Expand All @@ -174,6 +176,7 @@ public PluginsSettings(IConfigurationSection section)
if (section.Exists())
{
DownloadUrl = section.GetValue(nameof(DownloadUrl), DownloadUrl)!;
CustomUrls = section.GetSection(nameof(CustomUrls)).GetChildren().Select(p => new Uri(p.Value)).ToArray();
#if DEBUG
Prerelease = section.GetValue(nameof(Prerelease), Prerelease);
Version = section.GetValue(nameof(Version), Version)!;
Expand Down
3 changes: 2 additions & 1 deletion src/Neo.CLI/config.fs.mainnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"NeoNameService": "0x7061fbd31562664b58f422c3dee4acfd70dba8af"
},
"Plugins": {
"DownloadUrl": "https://api.github.com/repos/neo-project/neo-modules/releases"
"DownloadUrl": "https://api.github.com/repos/neo-project/neo/releases",
"CustomUrls": []
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that the CustomUrls is not so important in my opinion, the important thing now is to show the installed plugins correctly, if they are custom.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They can just change this download URL, and release on their github already.

}
},
"ProtocolConfiguration": {
Expand Down
3 changes: 2 additions & 1 deletion src/Neo.CLI/config.fs.testnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"NeoNameService": "0xfb08ccf30ab534a871b7b092a49fe70c154ed678"
},
"Plugins": {
"DownloadUrl": "https://api.github.com/repos/neo-project/neo-modules/releases"
"DownloadUrl": "https://api.github.com/repos/neo-project/neo/releases",
"CustomUrls": []
}
},
"ProtocolConfiguration": {
Expand Down
7 changes: 4 additions & 3 deletions src/Neo.CLI/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
"Active": false
},
"Storage": {
"Engine": "LevelDBStore", // Candidates [MemoryStore, LevelDBStore, RocksDBStore]
"Path": "Data_LevelDB_{0}" // {0} is a placeholder for the network id
"Engine": "LevelDBStore",
"Path": "Data_LevelDB_{0}"
},
"P2P": {
"Port": 10333,
Expand All @@ -24,7 +24,8 @@
"NeoNameService": "0x50ac1c37690cc2cfc594472833cf57505d5f46de"
},
"Plugins": {
"DownloadUrl": "https://api.github.com/repos/neo-project/neo-modules/releases"
"DownloadUrl": "https://api.github.com/repos/neo-project/neo/releases",
"CustomUrls": []
}
},
"ProtocolConfiguration": {
Expand Down
15 changes: 10 additions & 5 deletions src/Neo.CLI/config.json.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# README for Application and Protocol Configuration JSON File

This README provides an explanation for each field in the JSON configuration file for a NEO node.
This README provides an explanation for each field in the JSON configuration file for a Neo node.

## ApplicationConfiguration

Expand Down Expand Up @@ -31,15 +31,20 @@ This README provides an explanation for each field in the JSON configuration fil
- **NeoNameService**: Script hash of the Neo Name Service contract. MainNet is `0x50ac1c37690cc2cfc594472833cf57505d5f46de`, TestNet is `0x50ac1c37690cc2cfc594472833cf57505d5f46de`.

### Plugins
- **DownloadUrl**: URL to download plugins, typically from the NEO project's GitHub releases. Default is `https://api.github.com/repos/neo-project/neo/releases`.
- **DownloadUrl**: URL to download plugins, typically from the Neo project's GitHub releases. Default is `https://api.github.com/repos/neo-project/neo/releases`.
- **CustomUrls**: List of custom URLs for downloading plugins.

<div style="border: 1px solid #f8d7da; background-color: #f8d7da; color: #721c24; padding: 10px; border-radius: 5px;">
<strong>Warning:</strong> Plugin from the `DownloadUrl` will be installed if different plugins with the same name exist in different urls.
</div>

## ProtocolConfiguration

### Network
- **Network**: Network ID for the NEO network. MainNet is `860833102`, TestNet is `894710606`
- **Network**: Network ID for the Neo network. MainNet is `860833102`, TestNet is `894710606`

### AddressVersion
- **AddressVersion**: Version byte used in NEO address generation. Default is `53`.
- **AddressVersion**: Version byte used in Neo address generation. Default is `53`.

### MillisecondsPerBlock
- **MillisecondsPerBlock**: Time interval between blocks in milliseconds. Default is `15000` (15 seconds).
Expand Down Expand Up @@ -82,4 +87,4 @@ This README provides an explanation for each field in the JSON configuration fil
- `seed4t5.neo.org:20333`
- `seed5t5.neo.org:20333`

This configuration file is essential for setting up and running a NEO node, ensuring proper logging, storage, network connectivity, and consensus protocol parameters.
This configuration file is essential for setting up and running a Neo node, ensuring proper logging, storage, network connectivity, and consensus protocol parameters.
3 changes: 2 additions & 1 deletion src/Neo.CLI/config.mainnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"NeoNameService": "0x50ac1c37690cc2cfc594472833cf57505d5f46de"
},
"Plugins": {
"DownloadUrl": "https://api.github.com/repos/neo-project/neo-modules/releases"
"DownloadUrl": "https://api.github.com/repos/neo-project/neo/releases",
"CustomUrls": []
}
},
"ProtocolConfiguration": {
Expand Down
3 changes: 2 additions & 1 deletion src/Neo.CLI/config.testnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"NeoNameService": "0x50ac1c37690cc2cfc594472833cf57505d5f46de"
},
"Plugins": {
"DownloadUrl": "https://api.github.com/repos/neo-project/neo-modules/releases"
"DownloadUrl": "https://api.github.com/repos/neo-project/neo/releases",
"CustomUrls": []
}
},
"ProtocolConfiguration": {
Expand Down