✨ Affliation spell CRUD
This commit is contained in:
@@ -26,6 +26,7 @@ public class MagicSpellService(
|
|||||||
Dictionary<string, object> meta,
|
Dictionary<string, object> meta,
|
||||||
Instant? expiredAt = null,
|
Instant? expiredAt = null,
|
||||||
Instant? affectedAt = null,
|
Instant? affectedAt = null,
|
||||||
|
string? code = null,
|
||||||
bool preventRepeat = false
|
bool preventRepeat = false
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@@ -41,7 +42,7 @@ public class MagicSpellService(
|
|||||||
return existingSpell;
|
return existingSpell;
|
||||||
}
|
}
|
||||||
|
|
||||||
var spellWord = _GenerateRandomString(128);
|
var spellWord = code ?? _GenerateRandomString(128);
|
||||||
var spell = new SnMagicSpell
|
var spell = new SnMagicSpell
|
||||||
{
|
{
|
||||||
Spell = spellWord,
|
Spell = spellWord,
|
||||||
|
|||||||
111
DysonNetwork.Pass/Affiliation/AffiliationSpellController.cs
Normal file
111
DysonNetwork.Pass/Affiliation/AffiliationSpellController.cs
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
using DysonNetwork.Shared.Models;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace DysonNetwork.Pass.Affiliation;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Route("/api/affiliations")]
|
||||||
|
public class AffiliationSpellController(AppDatabase db) : ControllerBase
|
||||||
|
{
|
||||||
|
[HttpGet("me")]
|
||||||
|
[Authorize]
|
||||||
|
public async Task<ActionResult<List<SnAffiliationSpell>>> ListCreatedSpells(
|
||||||
|
[FromQuery(Name = "order")] string orderBy = "date",
|
||||||
|
[FromQuery(Name = "desc")] bool orderDesc = false,
|
||||||
|
[FromQuery] int take = 10,
|
||||||
|
[FromQuery] int offset = 0
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||||
|
|
||||||
|
var queryable = db.AffiliationSpells
|
||||||
|
.Where(s => s.AccountId == currentUser.Id)
|
||||||
|
.AsQueryable();
|
||||||
|
|
||||||
|
queryable = orderBy switch
|
||||||
|
{
|
||||||
|
"usage" => orderDesc
|
||||||
|
? queryable.OrderByDescending(q => q.Results.Count)
|
||||||
|
: queryable.OrderBy(q => q.Results.Count),
|
||||||
|
_ => orderDesc
|
||||||
|
? queryable.OrderByDescending(q => q.CreatedAt)
|
||||||
|
: queryable.OrderBy(q => q.CreatedAt)
|
||||||
|
};
|
||||||
|
|
||||||
|
var totalCount = queryable.Count();
|
||||||
|
Response.Headers["X-Total"] = totalCount.ToString();
|
||||||
|
|
||||||
|
var spells = await queryable
|
||||||
|
.Skip(offset)
|
||||||
|
.Take(take)
|
||||||
|
.ToListAsync();
|
||||||
|
return Ok(spells);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id:guid}")]
|
||||||
|
[Authorize]
|
||||||
|
public async Task<ActionResult<SnAffiliationSpell>> GetSpell([FromRoute] Guid id)
|
||||||
|
{
|
||||||
|
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||||
|
|
||||||
|
var spell = await db.AffiliationSpells
|
||||||
|
.Where(s => s.AccountId == currentUser.Id)
|
||||||
|
.Where(s => s.Id == id)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (spell is null) return NotFound();
|
||||||
|
|
||||||
|
return Ok(spell);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id:guid}/results")]
|
||||||
|
[Authorize]
|
||||||
|
public async Task<ActionResult<List<SnAffiliationResult>>> ListResults(
|
||||||
|
[FromRoute] Guid id,
|
||||||
|
[FromQuery(Name = "desc")] bool orderDesc = false,
|
||||||
|
[FromQuery] int take = 10,
|
||||||
|
[FromQuery] int offset = 0
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||||
|
|
||||||
|
var queryable = db.AffiliationResults
|
||||||
|
.Include(r => r.Spell)
|
||||||
|
.Where(r => r.Spell.AccountId == currentUser.Id)
|
||||||
|
.Where(r => r.SpellId == id)
|
||||||
|
.AsQueryable();
|
||||||
|
|
||||||
|
// Order by creation date
|
||||||
|
queryable = orderDesc
|
||||||
|
? queryable.OrderByDescending(r => r.CreatedAt)
|
||||||
|
: queryable.OrderBy(r => r.CreatedAt);
|
||||||
|
|
||||||
|
var totalCount = queryable.Count();
|
||||||
|
Response.Headers["X-Total"] = totalCount.ToString();
|
||||||
|
|
||||||
|
var results = await queryable
|
||||||
|
.Skip(offset)
|
||||||
|
.Take(take)
|
||||||
|
.ToListAsync();
|
||||||
|
return Ok(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("{id:guid}")]
|
||||||
|
[Authorize]
|
||||||
|
public async Task<ActionResult> DeleteSpell([FromRoute] Guid id)
|
||||||
|
{
|
||||||
|
if (HttpContext.Items["CurrentUser"] is not SnAccount currentUser) return Unauthorized();
|
||||||
|
|
||||||
|
var spell = await db.AffiliationSpells
|
||||||
|
.Where(s => s.AccountId == currentUser.Id)
|
||||||
|
.Where(s => s.Id == id)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
if (spell is null) return NotFound();
|
||||||
|
|
||||||
|
db.AffiliationSpells.Remove(spell);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
45
DysonNetwork.Pass/Affiliation/AffiliationSpellService.cs
Normal file
45
DysonNetwork.Pass/Affiliation/AffiliationSpellService.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using System.Security.Cryptography;
|
||||||
|
using DysonNetwork.Shared.Models;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace DysonNetwork.Pass.Affiliation;
|
||||||
|
|
||||||
|
public class AffiliationSpellService(AppDatabase db)
|
||||||
|
{
|
||||||
|
public async Task<SnAffiliationSpell> CreateAffiliationSpell(Guid accountId, string? spellWord)
|
||||||
|
{
|
||||||
|
spellWord ??= _GenerateRandomString(8);
|
||||||
|
var hasTaken = await db.AffiliationSpells.AnyAsync(s => s.Spell == spellWord);
|
||||||
|
if (hasTaken) throw new InvalidOperationException("The spell has been taken.");
|
||||||
|
|
||||||
|
var spell = new SnAffiliationSpell
|
||||||
|
{
|
||||||
|
AccountId = accountId,
|
||||||
|
Spell = spellWord
|
||||||
|
};
|
||||||
|
|
||||||
|
db.AffiliationSpells.Add(spell);
|
||||||
|
await db.SaveChangesAsync();
|
||||||
|
return spell;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<SnAffiliationSpell?> GetAffiliationSpell(string spellWord)
|
||||||
|
{
|
||||||
|
var spell = await db.AffiliationSpells.FirstOrDefaultAsync(s => s.Spell == spellWord);
|
||||||
|
return spell;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string _GenerateRandomString(int length)
|
||||||
|
{
|
||||||
|
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||||
|
var result = new char[length];
|
||||||
|
using var rng = RandomNumberGenerator.Create();
|
||||||
|
for (var i = 0; i < length; i++)
|
||||||
|
{
|
||||||
|
var bytes = new byte[1];
|
||||||
|
rng.GetBytes(bytes);
|
||||||
|
result[i] = chars[bytes[0] % chars.Length];
|
||||||
|
}
|
||||||
|
return new string(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -61,6 +61,9 @@ public class AppDatabase(
|
|||||||
public DbSet<SnLottery> Lotteries { get; set; } = null!;
|
public DbSet<SnLottery> Lotteries { get; set; } = null!;
|
||||||
public DbSet<SnLotteryRecord> LotteryRecords { get; set; } = null!;
|
public DbSet<SnLotteryRecord> LotteryRecords { get; set; } = null!;
|
||||||
|
|
||||||
|
public DbSet<SnAffiliationSpell> AffiliationSpells { get; set; } = null!;
|
||||||
|
public DbSet<SnAffiliationResult> AffiliationResults { get; set; } = null!;
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
{
|
{
|
||||||
optionsBuilder.UseNpgsql(
|
optionsBuilder.UseNpgsql(
|
||||||
|
|||||||
2874
DysonNetwork.Pass/Migrations/20251201145617_AddAffiliationSpell.Designer.cs
generated
Normal file
2874
DysonNetwork.Pass/Migrations/20251201145617_AddAffiliationSpell.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,90 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using NodaTime;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace DysonNetwork.Pass.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddAffiliationSpell : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "affiliation_spells",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
spell = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: false),
|
||||||
|
type = table.Column<int>(type: "integer", nullable: false),
|
||||||
|
expires_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true),
|
||||||
|
affected_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true),
|
||||||
|
meta = table.Column<Dictionary<string, object>>(type: "jsonb", nullable: false),
|
||||||
|
account_id = table.Column<Guid>(type: "uuid", nullable: true),
|
||||||
|
created_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
|
||||||
|
updated_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
|
||||||
|
deleted_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_affiliation_spells", x => x.id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "fk_affiliation_spells_accounts_account_id",
|
||||||
|
column: x => x.account_id,
|
||||||
|
principalTable: "accounts",
|
||||||
|
principalColumn: "id");
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "affiliation_results",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
resource_identifier = table.Column<string>(type: "character varying(8192)", maxLength: 8192, nullable: false),
|
||||||
|
spell_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||||
|
created_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
|
||||||
|
updated_at = table.Column<Instant>(type: "timestamp with time zone", nullable: false),
|
||||||
|
deleted_at = table.Column<Instant>(type: "timestamp with time zone", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk_affiliation_results", x => x.id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "fk_affiliation_results_affiliation_spells_spell_id",
|
||||||
|
column: x => x.spell_id,
|
||||||
|
principalTable: "affiliation_spells",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_affiliation_results_spell_id",
|
||||||
|
table: "affiliation_results",
|
||||||
|
column: "spell_id");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_affiliation_spells_account_id",
|
||||||
|
table: "affiliation_spells",
|
||||||
|
column: "account_id");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "ix_affiliation_spells_spell",
|
||||||
|
table: "affiliation_spells",
|
||||||
|
column: "spell",
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "affiliation_results");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "affiliation_spells");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -712,6 +712,103 @@ namespace DysonNetwork.Pass.Migrations
|
|||||||
b.ToTable("action_logs", (string)null);
|
b.ToTable("action_logs", (string)null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DysonNetwork.Shared.Models.SnAffiliationResult", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<Instant>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created_at");
|
||||||
|
|
||||||
|
b.Property<Instant?>("DeletedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("deleted_at");
|
||||||
|
|
||||||
|
b.Property<string>("ResourceIdentifier")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(8192)
|
||||||
|
.HasColumnType("character varying(8192)")
|
||||||
|
.HasColumnName("resource_identifier");
|
||||||
|
|
||||||
|
b.Property<Guid>("SpellId")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasColumnName("spell_id");
|
||||||
|
|
||||||
|
b.Property<Instant>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated_at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_affiliation_results");
|
||||||
|
|
||||||
|
b.HasIndex("SpellId")
|
||||||
|
.HasDatabaseName("ix_affiliation_results_spell_id");
|
||||||
|
|
||||||
|
b.ToTable("affiliation_results", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DysonNetwork.Shared.Models.SnAffiliationSpell", b =>
|
||||||
|
{
|
||||||
|
b.Property<Guid>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<Guid?>("AccountId")
|
||||||
|
.HasColumnType("uuid")
|
||||||
|
.HasColumnName("account_id");
|
||||||
|
|
||||||
|
b.Property<Instant?>("AffectedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("affected_at");
|
||||||
|
|
||||||
|
b.Property<Instant>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created_at");
|
||||||
|
|
||||||
|
b.Property<Instant?>("DeletedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("deleted_at");
|
||||||
|
|
||||||
|
b.Property<Instant?>("ExpiresAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("expires_at");
|
||||||
|
|
||||||
|
b.Property<Dictionary<string, object>>("Meta")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("jsonb")
|
||||||
|
.HasColumnName("meta");
|
||||||
|
|
||||||
|
b.Property<string>("Spell")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("character varying(1024)")
|
||||||
|
.HasColumnName("spell");
|
||||||
|
|
||||||
|
b.Property<int>("Type")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("type");
|
||||||
|
|
||||||
|
b.Property<Instant>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated_at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk_affiliation_spells");
|
||||||
|
|
||||||
|
b.HasIndex("AccountId")
|
||||||
|
.HasDatabaseName("ix_affiliation_spells_account_id");
|
||||||
|
|
||||||
|
b.HasIndex("Spell")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("ix_affiliation_spells_spell");
|
||||||
|
|
||||||
|
b.ToTable("affiliation_spells", (string)null);
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnApiKey", b =>
|
modelBuilder.Entity("DysonNetwork.Shared.Models.SnApiKey", b =>
|
||||||
{
|
{
|
||||||
b.Property<Guid>("Id")
|
b.Property<Guid>("Id")
|
||||||
@@ -2366,6 +2463,28 @@ namespace DysonNetwork.Pass.Migrations
|
|||||||
b.Navigation("Account");
|
b.Navigation("Account");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DysonNetwork.Shared.Models.SnAffiliationResult", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DysonNetwork.Shared.Models.SnAffiliationSpell", "Spell")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("SpellId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired()
|
||||||
|
.HasConstraintName("fk_affiliation_results_affiliation_spells_spell_id");
|
||||||
|
|
||||||
|
b.Navigation("Spell");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("DysonNetwork.Shared.Models.SnAffiliationSpell", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("DysonNetwork.Shared.Models.SnAccount", "Account")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("AccountId")
|
||||||
|
.HasConstraintName("fk_affiliation_spells_accounts_account_id");
|
||||||
|
|
||||||
|
b.Navigation("Account");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("DysonNetwork.Shared.Models.SnApiKey", b =>
|
modelBuilder.Entity("DysonNetwork.Shared.Models.SnApiKey", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("DysonNetwork.Shared.Models.SnAccount", "Account")
|
b.HasOne("DysonNetwork.Shared.Models.SnAccount", "Account")
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ public enum MagicSpellType
|
|||||||
AccountDeactivation,
|
AccountDeactivation,
|
||||||
AccountRemoval,
|
AccountRemoval,
|
||||||
AuthPasswordReset,
|
AuthPasswordReset,
|
||||||
ContactVerification,
|
ContactVerification
|
||||||
}
|
}
|
||||||
|
|
||||||
[Index(nameof(Spell), IsUnique = true)]
|
[Index(nameof(Spell), IsUnique = true)]
|
||||||
@@ -28,3 +28,39 @@ public class SnMagicSpell : ModelBase
|
|||||||
public Guid? AccountId { get; set; }
|
public Guid? AccountId { get; set; }
|
||||||
public SnAccount? Account { get; set; }
|
public SnAccount? Account { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum AffiliationSpellType
|
||||||
|
{
|
||||||
|
RegistrationInvite
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Different from the magic spell, this is for the regeneration invite and other marketing usage.
|
||||||
|
/// </summary>
|
||||||
|
[Index(nameof(Spell), IsUnique = true)]
|
||||||
|
public class SnAffiliationSpell : ModelBase
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; } = Guid.NewGuid();
|
||||||
|
[MaxLength(1024)] public string Spell { get; set; } = null!;
|
||||||
|
public AffiliationSpellType Type { get; set; }
|
||||||
|
public Instant? ExpiresAt { get; set; }
|
||||||
|
public Instant? AffectedAt { get; set; }
|
||||||
|
[Column(TypeName = "jsonb")] public Dictionary<string, object> Meta { get; set; } = new();
|
||||||
|
|
||||||
|
public List<SnAffiliationResult> Results = [];
|
||||||
|
|
||||||
|
public Guid? AccountId { get; set; }
|
||||||
|
public SnAccount? Account { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The record for who used the affiliation spells
|
||||||
|
/// </summary>
|
||||||
|
public class SnAffiliationResult : ModelBase
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; } = Guid.NewGuid();
|
||||||
|
[MaxLength(8192)] public string ResourceIdentifier { get; set; } = null!;
|
||||||
|
|
||||||
|
public Guid SpellId { get; set; }
|
||||||
|
[JsonIgnore] public SnAffiliationSpell Spell { get; set; } = null!;
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAccessToken_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003Fb370f448e9f5fca62da785172d83a214319335e27ac4d51840349c6dce15d68_003FAccessToken_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAccessToken_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003Fb370f448e9f5fca62da785172d83a214319335e27ac4d51840349c6dce15d68_003FAccessToken_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AActionResult_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E3_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F4f76d3d319d4497595e4095b28237676214908_003Ff8_003Fd5e7c1c7_003FActionResult_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAesGcm_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F3d932a3ff98244208ca84309a75a7734243600_003F2c_003F1063867b_003FAesGcm_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAesGcm_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F3d932a3ff98244208ca84309a75a7734243600_003F2c_003F1063867b_003FAesGcm_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAny_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F331aca3f6f414013b09964063341351379060_003F67_003F87f868e3_003FAny_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAny_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F331aca3f6f414013b09964063341351379060_003F67_003F87f868e3_003FAny_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AApnSender_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6aadc2cf048f477d8636fb2def7b73648200_003Fc5_003F2a1973a9_003FApnSender_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AApnSender_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003FLibrary_003FApplication_0020Support_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F6aadc2cf048f477d8636fb2def7b73648200_003Fc5_003F2a1973a9_003FApnSender_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
|||||||
Reference in New Issue
Block a user