diff --git a/src/Data/Models/Node.cs b/src/Data/Models/Node.cs
index c42f68ba..2a8ae58d 100644
--- a/src/Data/Models/Node.cs
+++ b/src/Data/Models/Node.cs
@@ -51,6 +51,11 @@ public class Node : Entity
public Wallet? ReturningFundsWallet { get; set; }
+ ///
+ /// enable/disable node
+ ///
+ public bool IsNodeDisabled { get; set; }
+
///
/// Returns true if the node is managed by us. We defer this from the existence of an Endpoint
///
diff --git a/src/Data/Repositories/NodeRepository.cs b/src/Data/Repositories/NodeRepository.cs
index 0c20bc2c..66b24c45 100644
--- a/src/Data/Repositories/NodeRepository.cs
+++ b/src/Data/Repositories/NodeRepository.cs
@@ -67,6 +67,7 @@ public NodeRepository(IRepository repository,
.Include(x => x.ReturningFundsWallet)
.SingleOrDefaultAsync(x => x.PubKey == key);
}
+
public async Task GetOrCreateByPubKey(string pubKey, ILightningService lightningService)
{
@@ -113,15 +114,17 @@ public async Task> GetAllManagedByNodeGuard()
{
await using var applicationDbContext = await _dbContextFactory.CreateDbContextAsync();
- var resultAsync = await applicationDbContext.Nodes
+ var query = applicationDbContext.Nodes
.Include(x => x.ReturningFundsWallet)
.ThenInclude(x => x.Keys)
- .Where(node => node.Endpoint != null)
- .ToListAsync();
+ .Include(x => x.ReturningFundsWallet)
+ .Where(node => node.Endpoint != null);
+
+ var resultAsync = await query.ToListAsync();
return resultAsync;
}
-
+
public async Task> GetAllManagedByUser(string userId)
{
await using var applicationDbContext = await _dbContextFactory.CreateDbContextAsync();
diff --git a/src/Helpers/JobTypes.cs b/src/Helpers/JobTypes.cs
index 9fb3d026..ebe93881 100644
--- a/src/Helpers/JobTypes.cs
+++ b/src/Helpers/JobTypes.cs
@@ -18,6 +18,8 @@
*/
using Quartz;
+using Quartz.Impl;
+using Quartz.Impl.Matchers;
using Quartz.Impl.Triggers;
namespace NodeGuard.Helpers;
@@ -33,9 +35,9 @@ public class SimpleJob
public static JobAndTrigger Create(JobDataMap data, string identitySuffix) where T : IJob
{
var job = JobBuilder.Create()
- .WithIdentity($"{typeof(T).Name}-{identitySuffix}")
- .SetJobData(data ?? new JobDataMap())
- .Build();
+ .WithIdentity($"{typeof(T).Name}-{identitySuffix}")
+ .SetJobData(data ?? new JobDataMap())
+ .Build();
var trigger = TriggerBuilder.Create()
.WithIdentity($"{typeof(T).Name}Trigger-{identitySuffix}")
@@ -44,6 +46,46 @@ public static JobAndTrigger Create(JobDataMap data, string identitySuffix) wh
return new JobAndTrigger(job, trigger);
}
+
+ public static async Task Reschedule(IScheduler scheduler, string identitySuffix) where T : IJob
+ {
+ try
+ {
+ var triggerKey = new TriggerKey($"{typeof(T).Name}-{identitySuffix}");
+
+ var trigger = TriggerBuilder.Create()
+ .WithIdentity($"{typeof(T).Name}Trigger-{identitySuffix}")
+ .StartNow()
+ .Build();
+
+ await scheduler.RescheduleJob(triggerKey, trigger);
+
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Unexpected error rescheduling job of type {typeof(T).Name} with identity suffix {identitySuffix}: {ex.Message}");
+ }
+ }
+
+ public static async Task DeleteJob(IScheduler scheduler, string identitySuffix) where T : IJob
+ {
+ try
+ {
+ JobKey jobKey = new JobKey($"{typeof(T).Name}-{identitySuffix}");
+ await scheduler.DeleteJob(jobKey);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Unexpected error occurred while removing the job of type {typeof(T).Name} with identity suffix {identitySuffix}: {ex.Message}");
+ }
+ }
+
+ public static async Task IsJobExists(IScheduler scheduler, string identitySuffix) where T : IJob
+ {
+ JobKey jobKey = new JobKey($"{typeof(T).Name}-{identitySuffix}");
+ return await scheduler.CheckExists(jobKey);
+ }
+
}
public class RetriableJob
@@ -55,7 +97,8 @@ public class RetriableJob
/// A suffix to identify a specific job, triggered from the same class
/// An optional list of retry intervals in minutes, defaults to { 1, 5, 10, 20 }
/// An object with a job and a trigger to pass to a scheduler
- public static JobAndTrigger Create(JobDataMap data, string identitySuffix, int[]? intervalListInMinutes = null) where T : IJob
+ public static JobAndTrigger Create(JobDataMap data, string identitySuffix, int[]? intervalListInMinutes = null)
+ where T : IJob
{
intervalListInMinutes = intervalListInMinutes ?? new int[] { 1, 5, 10, 20 };
@@ -63,10 +106,10 @@ public static JobAndTrigger Create(JobDataMap data, string identitySuffix, in
map.Put("intervalListInMinutes", intervalListInMinutes);
var job = JobBuilder.Create()
- .DisallowConcurrentExecution()
- .SetJobData(map)
- .WithIdentity($"{typeof(T).Name}-{identitySuffix}")
- .Build();
+ .DisallowConcurrentExecution()
+ .SetJobData(map)
+ .WithIdentity($"{typeof(T).Name}-{identitySuffix}")
+ .Build();
var trigger = TriggerBuilder.Create()
.WithIdentity($"{typeof(T).Name}Trigger-{identitySuffix}")
@@ -103,7 +146,7 @@ public static async Task Execute(IJobExecutionContext context, Func callba
if (intervals == null)
{
throw new Exception("No interval list found, make sure you're using the RetriableJob class");
- };
+ }
var trigger = context.Trigger as SimpleTriggerImpl;
@@ -134,7 +177,7 @@ public static async Task OnFail(IJobExecutionContext context, Func callbac
if (intervals == null)
{
throw new Exception("No interval list found, make sure you're using the RetriableJob class");
- };
+ }
var trigger = context.Trigger as SimpleTriggerImpl;
@@ -144,7 +187,7 @@ public static async Task OnFail(IJobExecutionContext context, Func callbac
await callback();
}
}
-
+
///
/// Get the next interval time
///
@@ -156,7 +199,7 @@ public static async Task OnFail(IJobExecutionContext context, Func callbac
if (intervals == null)
{
throw new Exception("No interval list found, make sure you're using the RetriableJob class");
- };
+ }
var trigger = context.Trigger as SimpleTriggerImpl;
@@ -166,7 +209,7 @@ public static async Task OnFail(IJobExecutionContext context, Func callbac
}
return null;
- }
+ }
}
public class JobAndTrigger
@@ -180,10 +223,12 @@ public JobAndTrigger(IJobDetail job, ITrigger trigger)
{
throw new Exception("Job parameter is needed");
}
+
if (trigger == null)
{
throw new Exception("Trigger parameter is needed");
}
+
Job = job;
Trigger = trigger;
}
diff --git a/src/Migrations/20231205153253_IsNodeDisabled.Designer.cs b/src/Migrations/20231205153253_IsNodeDisabled.Designer.cs
new file mode 100644
index 00000000..0790f82a
--- /dev/null
+++ b/src/Migrations/20231205153253_IsNodeDisabled.Designer.cs
@@ -0,0 +1,1263 @@
+//
+using System;
+using System.Collections.Generic;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using NodeGuard.Data;
+using NodeGuard.Helpers;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace NodeGuard.Migrations
+{
+ [DbContext(typeof(ApplicationDbContext))]
+ [Migration("20231205153253_IsNodeDisabled")]
+ partial class IsNodeDisabled
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "7.0.14")
+ .HasAnnotation("Relational:MaxIdentifierLength", 63);
+
+ NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
+
+ modelBuilder.Entity("ApplicationUserNode", b =>
+ {
+ b.Property("NodesId")
+ .HasColumnType("integer");
+
+ b.Property("UsersId")
+ .HasColumnType("text");
+
+ b.HasKey("NodesId", "UsersId");
+
+ b.HasIndex("UsersId");
+
+ b.ToTable("ApplicationUserNode");
+ });
+
+ modelBuilder.Entity("ChannelOperationRequestFMUTXO", b =>
+ {
+ b.Property("ChannelOperationRequestsId")
+ .HasColumnType("integer");
+
+ b.Property("UtxosId")
+ .HasColumnType("integer");
+
+ b.HasKey("ChannelOperationRequestsId", "UtxosId");
+
+ b.HasIndex("UtxosId");
+
+ b.ToTable("ChannelOperationRequestFMUTXO");
+ });
+
+ modelBuilder.Entity("FMUTXOWalletWithdrawalRequest", b =>
+ {
+ b.Property("UTXOsId")
+ .HasColumnType("integer");
+
+ b.Property("WalletWithdrawalRequestsId")
+ .HasColumnType("integer");
+
+ b.HasKey("UTXOsId", "WalletWithdrawalRequestsId");
+
+ b.HasIndex("WalletWithdrawalRequestsId");
+
+ b.ToTable("FMUTXOWalletWithdrawalRequest");
+ });
+
+ modelBuilder.Entity("KeyWallet", b =>
+ {
+ b.Property("KeysId")
+ .HasColumnType("integer");
+
+ b.Property("WalletsId")
+ .HasColumnType("integer");
+
+ b.HasKey("KeysId", "WalletsId");
+
+ b.HasIndex("WalletsId");
+
+ b.ToTable("KeyWallet");
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("text");
+
+ b.Property("Name")
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b.Property("NormalizedName")
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NormalizedName")
+ .IsUnique()
+ .HasDatabaseName("RoleNameIndex");
+
+ b.ToTable("AspNetRoles", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("ClaimType")
+ .HasColumnType("text");
+
+ b.Property("ClaimValue")
+ .HasColumnType("text");
+
+ b.Property("RoleId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("AspNetRoleClaims", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
+ {
+ b.Property("Id")
+ .HasColumnType("text");
+
+ b.Property("AccessFailedCount")
+ .HasColumnType("integer");
+
+ b.Property("ConcurrencyStamp")
+ .IsConcurrencyToken()
+ .HasColumnType("text");
+
+ b.Property("Discriminator")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Email")
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b.Property("EmailConfirmed")
+ .HasColumnType("boolean");
+
+ b.Property("LockoutEnabled")
+ .HasColumnType("boolean");
+
+ b.Property("LockoutEnd")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("NormalizedEmail")
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b.Property("NormalizedUserName")
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b.Property("PasswordHash")
+ .HasColumnType("text");
+
+ b.Property("PhoneNumber")
+ .HasColumnType("text");
+
+ b.Property("PhoneNumberConfirmed")
+ .HasColumnType("boolean");
+
+ b.Property("SecurityStamp")
+ .HasColumnType("text");
+
+ b.Property("TwoFactorEnabled")
+ .HasColumnType("boolean");
+
+ b.Property("UserName")
+ .HasMaxLength(256)
+ .HasColumnType("character varying(256)");
+
+ b.HasKey("Id");
+
+ b.HasIndex("NormalizedEmail")
+ .HasDatabaseName("EmailIndex");
+
+ b.HasIndex("NormalizedUserName")
+ .IsUnique()
+ .HasDatabaseName("UserNameIndex");
+
+ b.ToTable("AspNetUsers", (string)null);
+
+ b.HasDiscriminator("Discriminator").HasValue("IdentityUser");
+
+ b.UseTphMappingStrategy();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("ClaimType")
+ .HasColumnType("text");
+
+ b.Property("ClaimValue")
+ .HasColumnType("text");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AspNetUserClaims", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b =>
+ {
+ b.Property("LoginProvider")
+ .HasMaxLength(128)
+ .HasColumnType("character varying(128)");
+
+ b.Property("ProviderKey")
+ .HasMaxLength(128)
+ .HasColumnType("character varying(128)");
+
+ b.Property("ProviderDisplayName")
+ .HasColumnType("text");
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("LoginProvider", "ProviderKey");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AspNetUserLogins", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("text");
+
+ b.Property("RoleId")
+ .HasColumnType("text");
+
+ b.HasKey("UserId", "RoleId");
+
+ b.HasIndex("RoleId");
+
+ b.ToTable("AspNetUserRoles", (string)null);
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b =>
+ {
+ b.Property("UserId")
+ .HasColumnType("text");
+
+ b.Property("LoginProvider")
+ .HasMaxLength(128)
+ .HasColumnType("character varying(128)");
+
+ b.Property("Name")
+ .HasMaxLength(128)
+ .HasColumnType("character varying(128)");
+
+ b.Property("Value")
+ .HasColumnType("text");
+
+ b.HasKey("UserId", "LoginProvider", "Name");
+
+ b.ToTable("AspNetUserTokens", (string)null);
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.APIToken", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreationDatetime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("CreatorId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ExpirationDate")
+ .HasColumnType("timestamp without time zone");
+
+ b.Property("IsBlocked")
+ .HasColumnType("boolean");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("TokenHash")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("UpdateDatetime")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.HasIndex("CreatorId");
+
+ b.ToTable("ApiTokens");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.Channel", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("BtcCloseAddress")
+ .HasColumnType("text");
+
+ b.Property("ChanId")
+ .HasColumnType("numeric(20,0)");
+
+ b.Property("CreatedByNodeGuard")
+ .HasColumnType("boolean");
+
+ b.Property("CreationDatetime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DestinationNodeId")
+ .HasColumnType("integer");
+
+ b.Property("FundingTx")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("FundingTxOutputIndex")
+ .HasColumnType("bigint");
+
+ b.Property("IsAutomatedLiquidityEnabled")
+ .HasColumnType("boolean");
+
+ b.Property("IsPrivate")
+ .HasColumnType("boolean");
+
+ b.Property("SatsAmount")
+ .HasColumnType("bigint");
+
+ b.Property("SourceNodeId")
+ .HasColumnType("integer");
+
+ b.Property("Status")
+ .HasColumnType("integer");
+
+ b.Property("UpdateDatetime")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DestinationNodeId");
+
+ b.HasIndex("SourceNodeId");
+
+ b.ToTable("Channels");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.ChannelOperationRequest", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("AmountCryptoUnit")
+ .HasColumnType("integer");
+
+ b.Property("Changeless")
+ .HasColumnType("boolean");
+
+ b.Property("ChannelId")
+ .HasColumnType("integer");
+
+ b.Property("ClosingReason")
+ .HasColumnType("text");
+
+ b.Property("CreationDatetime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Description")
+ .HasColumnType("text");
+
+ b.Property("DestNodeId")
+ .HasColumnType("integer");
+
+ b.Property("FeeRate")
+ .HasColumnType("numeric");
+
+ b.Property("IsChannelPrivate")
+ .HasColumnType("boolean");
+
+ b.Property("JobId")
+ .HasColumnType("text");
+
+ b.Property("MempoolRecommendedFeesTypes")
+ .HasColumnType("integer");
+
+ b.Property("RequestType")
+ .HasColumnType("integer");
+
+ b.Property("SatsAmount")
+ .HasColumnType("bigint");
+
+ b.Property("SourceNodeId")
+ .HasColumnType("integer");
+
+ b.Property("Status")
+ .HasColumnType("integer");
+
+ b.Property>("StatusLogs")
+ .HasColumnType("jsonb");
+
+ b.Property("TxId")
+ .HasColumnType("text");
+
+ b.Property("UpdateDatetime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("UserId")
+ .HasColumnType("text");
+
+ b.Property("WalletId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ChannelId");
+
+ b.HasIndex("DestNodeId");
+
+ b.HasIndex("SourceNodeId");
+
+ b.HasIndex("UserId");
+
+ b.HasIndex("WalletId");
+
+ b.ToTable("ChannelOperationRequests");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.ChannelOperationRequestPSBT", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("ChannelOperationRequestId")
+ .HasColumnType("integer");
+
+ b.Property("CreationDatetime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("IsFinalisedPSBT")
+ .HasColumnType("boolean");
+
+ b.Property("IsInternalWalletPSBT")
+ .HasColumnType("boolean");
+
+ b.Property("IsTemplatePSBT")
+ .HasColumnType("boolean");
+
+ b.Property("PSBT")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("UpdateDatetime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("UserSignerId")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ChannelOperationRequestId");
+
+ b.HasIndex("UserSignerId");
+
+ b.ToTable("ChannelOperationRequestPSBTs");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.FMUTXO", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreationDatetime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("OutputIndex")
+ .HasColumnType("bigint");
+
+ b.Property("SatsAmount")
+ .HasColumnType("bigint");
+
+ b.Property("TxId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("UpdateDatetime")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.ToTable("FMUTXOs");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.InternalWallet", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreationDatetime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("DerivationPath")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("MasterFingerprint")
+ .HasColumnType("text");
+
+ b.Property("MnemonicString")
+ .HasColumnType("text");
+
+ b.Property("UpdateDatetime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("XPUB")
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.ToTable("InternalWallets");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.Key", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreationDatetime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Description")
+ .HasColumnType("text");
+
+ b.Property("InternalWalletId")
+ .HasColumnType("integer");
+
+ b.Property("IsArchived")
+ .HasColumnType("boolean");
+
+ b.Property("IsBIP39ImportedKey")
+ .HasColumnType("boolean");
+
+ b.Property("IsCompromised")
+ .HasColumnType("boolean");
+
+ b.Property("MasterFingerprint")
+ .HasColumnType("text");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("Path")
+ .HasColumnType("text");
+
+ b.Property("UpdateDatetime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("UserId")
+ .HasColumnType("text");
+
+ b.Property("XPUB")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("InternalWalletId");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("Keys");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.LiquidityRule", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("ChannelId")
+ .HasColumnType("integer");
+
+ b.Property("CreationDatetime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("IsReverseSwapWalletRule")
+ .HasColumnType("boolean");
+
+ b.Property("MinimumLocalBalance")
+ .HasColumnType("numeric");
+
+ b.Property("MinimumRemoteBalance")
+ .HasColumnType("numeric");
+
+ b.Property("NodeId")
+ .HasColumnType("integer");
+
+ b.Property("RebalanceTarget")
+ .HasColumnType("numeric");
+
+ b.Property("ReverseSwapAddress")
+ .HasColumnType("text");
+
+ b.Property("ReverseSwapWalletId")
+ .HasColumnType("integer");
+
+ b.Property("SwapWalletId")
+ .HasColumnType("integer");
+
+ b.Property("UpdateDatetime")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.HasIndex("ChannelId")
+ .IsUnique();
+
+ b.HasIndex("NodeId");
+
+ b.HasIndex("ReverseSwapWalletId");
+
+ b.HasIndex("SwapWalletId");
+
+ b.ToTable("LiquidityRules");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.Node", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("AutosweepEnabled")
+ .HasColumnType("boolean");
+
+ b.Property("ChannelAdminMacaroon")
+ .HasColumnType("text");
+
+ b.Property("CreationDatetime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Description")
+ .HasColumnType("text");
+
+ b.Property("Endpoint")
+ .HasColumnType("text");
+
+ b.Property("IsNodeDisabled")
+ .HasColumnType("boolean");
+
+ b.Property("JobId")
+ .HasColumnType("text");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("PubKey")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ReturningFundsWalletId")
+ .HasColumnType("integer");
+
+ b.Property("UpdateDatetime")
+ .HasColumnType("timestamp with time zone");
+
+ b.HasKey("Id");
+
+ b.HasIndex("PubKey")
+ .IsUnique();
+
+ b.HasIndex("ReturningFundsWalletId");
+
+ b.ToTable("Nodes");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.Wallet", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("BIP39Seedphrase")
+ .HasColumnType("text");
+
+ b.Property("CreationDatetime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Description")
+ .HasColumnType("text");
+
+ b.Property("ImportedOutputDescriptor")
+ .HasColumnType("text");
+
+ b.Property("InternalWalletId")
+ .HasColumnType("integer");
+
+ b.Property("InternalWalletMasterFingerprint")
+ .HasColumnType("text");
+
+ b.Property("InternalWalletSubDerivationPath")
+ .HasColumnType("text");
+
+ b.Property("IsArchived")
+ .HasColumnType("boolean");
+
+ b.Property("IsBIP39Imported")
+ .HasColumnType("boolean");
+
+ b.Property("IsCompromised")
+ .HasColumnType("boolean");
+
+ b.Property("IsFinalised")
+ .HasColumnType("boolean");
+
+ b.Property("IsHotWallet")
+ .HasColumnType("boolean");
+
+ b.Property("IsUnSortedMultiSig")
+ .HasColumnType("boolean");
+
+ b.Property("MofN")
+ .HasColumnType("integer");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("ReferenceId")
+ .HasColumnType("text");
+
+ b.Property("UpdateDatetime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("WalletAddressType")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("InternalWalletId");
+
+ b.HasIndex("InternalWalletSubDerivationPath", "InternalWalletMasterFingerprint")
+ .IsUnique();
+
+ b.ToTable("Wallets");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.WalletWithdrawalRequest", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("Amount")
+ .HasColumnType("numeric");
+
+ b.Property("Changeless")
+ .HasColumnType("boolean");
+
+ b.Property("CreationDatetime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("Description")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("DestinationAddress")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("JobId")
+ .HasColumnType("text");
+
+ b.Property("RejectCancelDescription")
+ .HasColumnType("text");
+
+ b.Property("RequestMetadata")
+ .HasColumnType("text");
+
+ b.Property("Status")
+ .HasColumnType("integer");
+
+ b.Property("TxId")
+ .HasColumnType("text");
+
+ b.Property("UpdateDatetime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("UserRequestorId")
+ .HasColumnType("text");
+
+ b.Property("WalletId")
+ .HasColumnType("integer");
+
+ b.Property("WithdrawAllFunds")
+ .HasColumnType("boolean");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserRequestorId");
+
+ b.HasIndex("WalletId");
+
+ b.ToTable("WalletWithdrawalRequests");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.WalletWithdrawalRequestPSBT", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CreationDatetime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("IsFinalisedPSBT")
+ .HasColumnType("boolean");
+
+ b.Property("IsInternalWalletPSBT")
+ .HasColumnType("boolean");
+
+ b.Property("IsTemplatePSBT")
+ .HasColumnType("boolean");
+
+ b.Property("PSBT")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.Property("SignerId")
+ .HasColumnType("text");
+
+ b.Property("UpdateDatetime")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("WalletWithdrawalRequestId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("SignerId");
+
+ b.HasIndex("WalletWithdrawalRequestId");
+
+ b.ToTable("WalletWithdrawalRequestPSBTs");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.ApplicationUser", b =>
+ {
+ b.HasBaseType("Microsoft.AspNetCore.Identity.IdentityUser");
+
+ b.HasDiscriminator().HasValue("ApplicationUser");
+ });
+
+ modelBuilder.Entity("ApplicationUserNode", b =>
+ {
+ b.HasOne("NodeGuard.Data.Models.Node", null)
+ .WithMany()
+ .HasForeignKey("NodesId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("NodeGuard.Data.Models.ApplicationUser", null)
+ .WithMany()
+ .HasForeignKey("UsersId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("ChannelOperationRequestFMUTXO", b =>
+ {
+ b.HasOne("NodeGuard.Data.Models.ChannelOperationRequest", null)
+ .WithMany()
+ .HasForeignKey("ChannelOperationRequestsId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("NodeGuard.Data.Models.FMUTXO", null)
+ .WithMany()
+ .HasForeignKey("UtxosId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("FMUTXOWalletWithdrawalRequest", b =>
+ {
+ b.HasOne("NodeGuard.Data.Models.FMUTXO", null)
+ .WithMany()
+ .HasForeignKey("UTXOsId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("NodeGuard.Data.Models.WalletWithdrawalRequest", null)
+ .WithMany()
+ .HasForeignKey("WalletWithdrawalRequestsId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("KeyWallet", b =>
+ {
+ b.HasOne("NodeGuard.Data.Models.Key", null)
+ .WithMany()
+ .HasForeignKey("KeysId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("NodeGuard.Data.Models.Wallet", null)
+ .WithMany()
+ .HasForeignKey("WalletsId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b =>
+ {
+ b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
+ .WithMany()
+ .HasForeignKey("RoleId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b =>
+ {
+ b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b =>
+ {
+ b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b =>
+ {
+ b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
+ .WithMany()
+ .HasForeignKey("RoleId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b =>
+ {
+ b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.APIToken", b =>
+ {
+ b.HasOne("NodeGuard.Data.Models.ApplicationUser", "Creator")
+ .WithMany()
+ .HasForeignKey("CreatorId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Creator");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.Channel", b =>
+ {
+ b.HasOne("NodeGuard.Data.Models.Node", "DestinationNode")
+ .WithMany()
+ .HasForeignKey("DestinationNodeId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("NodeGuard.Data.Models.Node", "SourceNode")
+ .WithMany()
+ .HasForeignKey("SourceNodeId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("DestinationNode");
+
+ b.Navigation("SourceNode");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.ChannelOperationRequest", b =>
+ {
+ b.HasOne("NodeGuard.Data.Models.Channel", "Channel")
+ .WithMany("ChannelOperationRequests")
+ .HasForeignKey("ChannelId");
+
+ b.HasOne("NodeGuard.Data.Models.Node", "DestNode")
+ .WithMany("ChannelOperationRequestsAsDestination")
+ .HasForeignKey("DestNodeId");
+
+ b.HasOne("NodeGuard.Data.Models.Node", "SourceNode")
+ .WithMany("ChannelOperationRequestsAsSource")
+ .HasForeignKey("SourceNodeId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("NodeGuard.Data.Models.ApplicationUser", "User")
+ .WithMany("ChannelOperationRequests")
+ .HasForeignKey("UserId");
+
+ b.HasOne("NodeGuard.Data.Models.Wallet", "Wallet")
+ .WithMany("ChannelOperationRequestsAsSource")
+ .HasForeignKey("WalletId");
+
+ b.Navigation("Channel");
+
+ b.Navigation("DestNode");
+
+ b.Navigation("SourceNode");
+
+ b.Navigation("User");
+
+ b.Navigation("Wallet");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.ChannelOperationRequestPSBT", b =>
+ {
+ b.HasOne("NodeGuard.Data.Models.ChannelOperationRequest", "ChannelOperationRequest")
+ .WithMany("ChannelOperationRequestPsbts")
+ .HasForeignKey("ChannelOperationRequestId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("NodeGuard.Data.Models.ApplicationUser", "UserSigner")
+ .WithMany()
+ .HasForeignKey("UserSignerId");
+
+ b.Navigation("ChannelOperationRequest");
+
+ b.Navigation("UserSigner");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.Key", b =>
+ {
+ b.HasOne("NodeGuard.Data.Models.InternalWallet", "InternalWallet")
+ .WithMany()
+ .HasForeignKey("InternalWalletId");
+
+ b.HasOne("NodeGuard.Data.Models.ApplicationUser", "User")
+ .WithMany("Keys")
+ .HasForeignKey("UserId");
+
+ b.Navigation("InternalWallet");
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.LiquidityRule", b =>
+ {
+ b.HasOne("NodeGuard.Data.Models.Channel", "Channel")
+ .WithMany("LiquidityRules")
+ .HasForeignKey("ChannelId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("NodeGuard.Data.Models.Node", "Node")
+ .WithMany()
+ .HasForeignKey("NodeId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("NodeGuard.Data.Models.Wallet", "ReverseSwapWallet")
+ .WithMany("LiquidityRulesAsReverseSwapWallet")
+ .HasForeignKey("ReverseSwapWalletId");
+
+ b.HasOne("NodeGuard.Data.Models.Wallet", "SwapWallet")
+ .WithMany("LiquidityRulesAsSwapWallet")
+ .HasForeignKey("SwapWalletId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Channel");
+
+ b.Navigation("Node");
+
+ b.Navigation("ReverseSwapWallet");
+
+ b.Navigation("SwapWallet");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.Node", b =>
+ {
+ b.HasOne("NodeGuard.Data.Models.Wallet", "ReturningFundsWallet")
+ .WithMany()
+ .HasForeignKey("ReturningFundsWalletId");
+
+ b.Navigation("ReturningFundsWallet");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.Wallet", b =>
+ {
+ b.HasOne("NodeGuard.Data.Models.InternalWallet", "InternalWallet")
+ .WithMany()
+ .HasForeignKey("InternalWalletId");
+
+ b.Navigation("InternalWallet");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.WalletWithdrawalRequest", b =>
+ {
+ b.HasOne("NodeGuard.Data.Models.ApplicationUser", "UserRequestor")
+ .WithMany("WalletWithdrawalRequests")
+ .HasForeignKey("UserRequestorId");
+
+ b.HasOne("NodeGuard.Data.Models.Wallet", "Wallet")
+ .WithMany()
+ .HasForeignKey("WalletId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("UserRequestor");
+
+ b.Navigation("Wallet");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.WalletWithdrawalRequestPSBT", b =>
+ {
+ b.HasOne("NodeGuard.Data.Models.ApplicationUser", "Signer")
+ .WithMany()
+ .HasForeignKey("SignerId");
+
+ b.HasOne("NodeGuard.Data.Models.WalletWithdrawalRequest", "WalletWithdrawalRequest")
+ .WithMany("WalletWithdrawalRequestPSBTs")
+ .HasForeignKey("WalletWithdrawalRequestId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Signer");
+
+ b.Navigation("WalletWithdrawalRequest");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.Channel", b =>
+ {
+ b.Navigation("ChannelOperationRequests");
+
+ b.Navigation("LiquidityRules");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.ChannelOperationRequest", b =>
+ {
+ b.Navigation("ChannelOperationRequestPsbts");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.Node", b =>
+ {
+ b.Navigation("ChannelOperationRequestsAsDestination");
+
+ b.Navigation("ChannelOperationRequestsAsSource");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.Wallet", b =>
+ {
+ b.Navigation("ChannelOperationRequestsAsSource");
+
+ b.Navigation("LiquidityRulesAsReverseSwapWallet");
+
+ b.Navigation("LiquidityRulesAsSwapWallet");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.WalletWithdrawalRequest", b =>
+ {
+ b.Navigation("WalletWithdrawalRequestPSBTs");
+ });
+
+ modelBuilder.Entity("NodeGuard.Data.Models.ApplicationUser", b =>
+ {
+ b.Navigation("ChannelOperationRequests");
+
+ b.Navigation("Keys");
+
+ b.Navigation("WalletWithdrawalRequests");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/src/Migrations/20231205153253_IsNodeDisabled.cs b/src/Migrations/20231205153253_IsNodeDisabled.cs
new file mode 100644
index 00000000..caf65b97
--- /dev/null
+++ b/src/Migrations/20231205153253_IsNodeDisabled.cs
@@ -0,0 +1,29 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace NodeGuard.Migrations
+{
+ ///
+ public partial class IsNodeDisabled : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "IsNodeDisabled",
+ table: "Nodes",
+ type: "boolean",
+ nullable: false,
+ defaultValue: false);
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "IsNodeDisabled",
+ table: "Nodes");
+ }
+ }
+}
diff --git a/src/Migrations/ApplicationDbContextModelSnapshot.cs b/src/Migrations/ApplicationDbContextModelSnapshot.cs
index 487c3bc1..73a9f9a9 100644
--- a/src/Migrations/ApplicationDbContextModelSnapshot.cs
+++ b/src/Migrations/ApplicationDbContextModelSnapshot.cs
@@ -19,7 +19,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
- .HasAnnotation("ProductVersion", "7.0.9")
+ .HasAnnotation("ProductVersion", "7.0.14")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
@@ -707,6 +707,9 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Property("Endpoint")
.HasColumnType("text");
+ b.Property("IsNodeDisabled")
+ .HasColumnType("boolean");
+
b.Property("JobId")
.HasColumnType("text");
diff --git a/src/Pages/Nodes.razor b/src/Pages/Nodes.razor
index 4d1fcf35..5b5095c2 100644
--- a/src/Pages/Nodes.razor
+++ b/src/Pages/Nodes.razor
@@ -27,16 +27,24 @@
@(context.EditState) node
-
+
+
+
+
+
+
+
+
+ Edit
+ Delete
+ @_NodesState(context.Item)
+
+
+
+
-
-
-
-
-
-
@@ -102,6 +110,7 @@
+
@@ -161,7 +170,7 @@
private bool _modalVisible;
private bool _hideDelete = true;
private bool _editable;
- private List _availableWallets = new List();
+ private List _availableWallets = new();
CancellationTokenSource cts = new();
@@ -183,6 +192,7 @@
public static readonly ColumnDefault Endpoint = new("Endpoint");
public static readonly ColumnDefault OutboundOpenChannels = new("Outbound Open Channels");
public static readonly ColumnDefault ReturningFundsWallet = new("Returning Funds Wallet");
+ public static readonly ColumnDefault Disabled = new("Disabled");
public static readonly ColumnDefault Autosweep = new("Autosweep");
public static readonly ColumnDefault CreationDate = new("Creation Date");
public static readonly ColumnDefault UpdateDate = new("Update Date");
@@ -204,6 +214,45 @@
}
}
}
+
+ private string _NodesState(Node node)
+ {
+ return node.IsNodeDisabled ? "Enable Node" : "Disable Node";
+ }
+
+ private async Task ToggleNodeStatus(Node node)
+ {
+ try
+ {
+ node.IsNodeDisabled = !node.IsNodeDisabled;
+ if (node.IsNodeDisabled)
+ {
+ await StopJob(node);
+ }
+ else
+ {
+ await CreateJobs(node);
+ }
+ node.JobId = null;
+ var (result, _) = NodeRepository.Update(node);
+ if (result)
+ {
+ _nodes = await NodeRepository.GetAllManagedByNodeGuard();
+ _availableWallets = await WalletRepository.GetAvailableWallets();
+ string status = node.IsNodeDisabled ? "disabled": "enabled";
+ string message = $"{node.Name} is {status}";
+ ToastService.ShowSuccess(message);
+ }
+ else
+ {
+ ToastService.ShowError($"Error while disabling the {node.Name}, please contact a superadmin for troubleshooting");
+ }
+ }
+ catch
+ {
+ ToastService.ShowError($"Error while disabling {node.Name}, please contact a superadmin for troubleshooting");
+ }
+ }
private async Task GetData()
{
@@ -235,9 +284,20 @@
}
else
{
- arg.Item.UpdateDatetime = DateTimeOffset.Now;
- arg.Item.CreationDatetime = DateTimeOffset.Now;
- var addResult = await NodeRepository.AddAsync(arg.Item);
+ Node newNode = arg.Item as Node;
+ (bool, string?) addResult;
+ var restoredNode = await NodeRepository.GetByPubkey(arg.Item.PubKey);
+
+ if (restoredNode != null)
+ {
+ newNode.Id = restoredNode.Id;
+ newNode.CreationDatetime = restoredNode.CreationDatetime;
+ addResult = NodeRepository.Update(newNode);
+ }
+ else
+ {
+ addResult = await NodeRepository.AddAsync(arg.Item);
+ }
if (addResult.Item1)
{
ToastService.ShowSuccess($"Node {arg.Item.Name} Created");
@@ -308,7 +368,10 @@
{
if (node != null)
{
- var (result, _) = NodeRepository.Remove(node);
+ node.IsNodeDisabled = true;
+ node.Endpoint = null;
+ node.ChannelAdminMacaroon = null;
+ var (result, _) = NodeRepository.Update(node);
if (!result)
{
ToastService.ShowError($"{node.Name} could not be deleted");
@@ -319,7 +382,7 @@
_nodes = await NodeRepository.GetAll();
}
-
+ await StopJob(node);
}
}
@@ -327,33 +390,73 @@
}
+
+ private async Task StopJob(Node node)
+ {
+ try
+ {
+ IScheduler scheduler = await SchedulerFactory.GetScheduler();
+
+ await SimpleJob.DeleteJob(scheduler, node.Id.ToString());
+ await SimpleJob.DeleteJob(scheduler, node.Id.ToString());
+
+ node.JobId = null;
+ }
+ catch
+ {
+ ToastService.ShowError("Error while stopping jobs, please contact a superadmin for troubleshooting");
+ }
+ }
+
private async Task CreateJobs(Node node)
{
try
{
IScheduler scheduler = await SchedulerFactory.GetScheduler();
+
+ await UpsertJob(scheduler, node, map =>
+ {
+ map.Put("managedNodeId", node.Id.ToString());
+ });
+
+ await UpsertJob(scheduler, node, map =>
+ {
+ map.Put("nodeId", node.Id);
+ });
+
+ ToastService.ShowSuccess("Node subscription job created");
+ }
+
+ catch
+ {
+ ToastService.ShowError("Error while requesting to open the channel, please contact a superadmin for troubleshooting");
+ }
+ }
+ private async Task UpsertJob(IScheduler scheduler, Node node, Action configureMap) where TJob : IJob
+ {
+ var isJobExists = await SimpleJob.IsJobExists(scheduler, node.Id.ToString());
+
+ if (isJobExists)
+ {
+ await SimpleJob.Reschedule