WIP /search

main
Paul Schneider 6 months ago
parent e229439174
commit aeecc524a4
34 changed files with 1188 additions and 391 deletions

39
.vscode/tasks.json vendored

@ -1,19 +1,6 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "restore",
"command": "dotnet",
"type": "process",
"args": [
"restore",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary",
"--ignore-failed-sources"
],
"problemMatcher": "$msCompile",
"group": "build"
},
{
"label": "build",
"command": "dotnet",
@ -46,21 +33,6 @@
"dependsOn":["build"],
"group": "test"
},
{
"label": "buildcli",
"command": "msbuild",
"type": "process",
"args": [
"src/isn",
"/p:Configuration=Debug",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary",
"/restore"
],
"problemMatcher": "$msCompile",
"group": "build"
},
{
"label": "publish",
"command": "dotnet",
@ -79,17 +51,6 @@
},
"group": "none"
},
{
"label": "monopublish",
"command": "msbuild",
"type": "process",
"args": [
"/t:publish",
"/p:TargetFramework=netcoreapp2.1;PublishDir=${workspaceFolder}/artiffacts;RuntimeIdentifier=linux-x64",
],
"problemMatcher": "$msCompile"
},
{
"label": "test",
"command": "dotnet",

@ -24,9 +24,21 @@ packs: pack-isn pack-isnd pack-isn.abst
clean: clean-isnd clean-isn clean-isn.abst
server-update:
dotnet build -c Release
dotnet build -c Release src/isnd
dotnet publish -c Release -f net7.0 src/isnd
sudo systemctl stop isnd
sudo cp -a src/isnd/bin/Release/net7.0/publish/* /srv/www/isnd
sudo systemctl start isnd
client-update:
dotnet build -c Release src/isn
# MAJ du client
sudo cp -a src/isn/bin/Release/net7.0/* /usr/local/lib/isn
sudo chown -R root:root /usr/local/lib/isn
src/isn.abst/bin/Release/isn.abst.1.0.24.nupkg:
dotnet pack src/isn.abst -c Release
push-test: src/isn.abst/bin/Release/isn.abst.1.0.24.nupkg
isn push -s "http://localhost:3002/v3/index.json" src/isn.abst/bin/Release/isn.abst.1.0.24.nupkg

@ -75,6 +75,8 @@ sudo ln -s /usr/local/lib/isn/isn /usr/local/bin/isn
### Mises à jour
Dans le détail, la séquence serait du style :
````bash
# compiler tout
dotnet build -c Release
@ -87,3 +89,11 @@ sudo systemctl start isnd
sudo cp -a src/isn/bin/Release/net7.0/* /usr/local/lib/isn
sudo chown -R root:root /usr/local/lib/isn
````
On pourra cibler "client-update" ou "server-update", à la construction :
````bash
make server-update
make client-update
````

@ -1,3 +1,4 @@
using System;
using isn.abst;
namespace isnd.Entities
@ -15,6 +16,8 @@ namespace isnd.Entities
public const string Content = "/content";
public const string Nuget = "/nuget";
public const string Find = "/index/FindPackagesById()"; // /FindPackagesById()?id='isn.abst'&semVerLevel=2.0.0
[Obsolete("use the V3 search")]
public const string V2Find = "/v2/FindPackagesById()"; // /FindPackagesById()??$filter=IsLatestVersion&$orderby=Version desc&$top=1&id='isn.abst'
}
}

@ -4,6 +4,11 @@ namespace isnd.Data.Catalog
{
public abstract class Permalink
{
public Permalink(string id)
{
Type = GetType().Name;
this.id = id;
}
public Permalink(string id, string type)
{
@ -11,11 +16,6 @@ namespace isnd.Data.Catalog
this.id = id;
}
public Permalink(string id)
{
Type = GetType().Name;
this.id = id;
}
[JsonProperty("@type")]
public virtual string Type { get; set; }
@ -23,7 +23,7 @@ namespace isnd.Data.Catalog
[JsonProperty("@id")]
public string Id { get => id; }
private readonly string id;
protected string id;
public string GetId() { return id; }

@ -2,7 +2,7 @@
<PropertyGroup>
<PackageVersion>1.0.1</PackageVersion>
<Version>1.0.7</Version>
<TargetFrameworks>net7.0</TargetFrameworks>
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
<NoWarn>NETSDK1138</NoWarn>
<AssemblyVersion>1.0.7.0</AssemblyVersion>
<FileVersion>1.0.7.0</FileVersion>

@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using Mono.Options;
using Newtonsoft.Json;
@ -16,12 +18,18 @@ namespace isn
if (cfgSettingIf.Exists)
{
var json = File.ReadAllText(cfgSettingIf.FullName);
settings = JsonConvert.DeserializeObject<Settings>(json);
CurrentSource = settings.DefaultSourceKey;
Settings = JsonConvert.DeserializeObject<Settings>(json);
CurrentSource = Settings.DefaultSourceKey;
}
if (Settings==null)
{
Settings= Settings.Create();
}
rsa = RSA.Create(Settings.RSAParameters);
}
static readonly OptionSet storeoptions = new OptionSet {
{ "s|source=", "use source", val => CurrentSource ??= val },
{ "s|source=", "use source", val => Settings.CurrentSourceKey ??= val },
{ "h|help", "show this message and exit", h => shouldShowPushHelp = h != null },
};
@ -38,11 +46,9 @@ namespace isn
{ "v|version", "show soft version info and exit", h => shouldShowVersion = h != null }
};
static string apiKey;
static readonly OptionSet pushoptions = new OptionSet {
{ "k|api-key=", "use api key", val => apiKey = apiKey ?? val },
{ "s|source=", "use source", val => CurrentSource = CurrentSource ?? val },
{ "k|api-key=", "use api key", val => Settings.CurrentSource.SetApiKey(rsa, val)},
{ "s|source=", "use source", val => Settings.CurrentSourceKey = val },
{ "h|help", "show this message and exit", h => shouldShowPushHelp = h != null },
};
static readonly OptionSet sourceoptions = new OptionSet {
@ -57,31 +63,21 @@ namespace isn
private static bool shouldShowVersion;
private static bool shouldShowSourceHelp;
private static bool shouldShowPushHelp;
static Settings settings = null;
public static RSA rsa;
public static Settings Settings
{
get
{
if (settings == null)
LoadConfig();
if (settings == null)
{
settings = new Settings
{
DataProtectionTitle = "isn",
Sources = new Dictionary<string, SourceSettings>()
};
}
return settings;
}
get; set;
}
public static string CurrentSource { get => Settings.CurrentSourceKey; set => Settings.CurrentSourceKey = value; }
static int Main(string[] args)
{
LoadConfig();
var commandSet = new CommandSet("isn");
var srclst = new Command("list")
{
@ -213,7 +209,6 @@ namespace isn
Console.WriteLine("isn version " + GitVersionInformation.AssemblySemFileVer);
}
return commandSet.Run(args);
}
}
}

@ -9,7 +9,6 @@ namespace isn
{
public class SourceSettings
{
private RSA rsa;
/// <summary>
/// Protected API Key
@ -25,26 +24,38 @@ namespace isn
public SourceSettings()
{
rsa = RSA.Create();
}
public string GetClearApiKey()
public string GetClearApiKey(RSA rsa)
{
var base64EncodedBytes = System.Convert.FromBase64String(ProtectedApiKey);
var decrypted = rsa.Decrypt(base64EncodedBytes, RSAEncryptionPadding.Pkcs1);
return Encoding.UTF8.GetString(decrypted);
var decrypted = rsa.Decrypt(System.Convert.FromBase64String(ProtectedApiKey), RSAEncryptionPadding.Pkcs1);
return Encoding.Default.GetString(decrypted);
}
public void SetApiKey(string key)
public void SetApiKey(RSA rsa, string key)
{
var crypted =rsa.Encrypt(Encoding.UTF8.GetBytes(key), RSAEncryptionPadding.Pkcs1);
ProtectedApiKey = System.Convert.ToBase64String(crypted);
var ciphered =rsa.Encrypt(Encoding.Default.GetBytes(key), RSAEncryptionPadding.Pkcs1);
ProtectedApiKey = System.Convert.ToBase64String(ciphered);
}
}
public class Settings
{
public string DataProtectionTitle { get; set; }
private Settings()
{
}
public static Settings Create()
{
var rsaParams = CreateCipheringParameters();
return new Settings {
RSAParameters = rsaParams,
Sources = new Dictionary<string, SourceSettings>()
};
}
public RSAParameters RSAParameters { get; set; }
public Dictionary<string, SourceSettings> Sources { get; set; }
private string defSourceKey;
@ -58,30 +69,24 @@ namespace isn
get => defSourceKey;
set
{
if (!Sources.ContainsKey(value))
{
Sources[value]=new SourceSettings
{
Url = defSourceKey
};
}
defSourceKey = value;
}
}
string currentSourceKey;
[JsonIgnore, NotMapped]
public string CurrentSourceKey
public string CurrentSourceKey {get; set;}
private static RSAParameters CreateCipheringParameters()
{
get {
return currentSourceKey;
}
set{
if (!Sources.ContainsKey(value))
throw new InvalidOperationException($"source is invalid ({value})");
currentSourceKey = value;
}
var provider = new RSACryptoServiceProvider(2048);
return provider.ExportParameters(true);
}
[JsonIgnore, NotMapped]
public SourceSettings CurrentSource
{
get => this.Sources[CurrentSourceKey];
}
}
}

@ -17,7 +17,7 @@ namespace isn
this.settings = settings;
}
public PushReport Run(string pkg, string source, string apikey)
public PushReport Run(string pkg, string source, string apiKey)
{
var resources = SourceHelpers.GetServerResources(source);
@ -33,25 +33,30 @@ namespace isn
var report = new PushReport
{
PkgName = fi.Name,
Message = "Le fichier n'existe pas : " + fi.FullName
Message = "The package does not exist : " + fi.FullName
};
return report;
}
using (var client = new HttpClient())
using (var client = new HttpClient(
new HttpClientHandler
{
AllowAutoRedirect = false
}
))
try
{
return client.UploadFilesToServer(new Uri(pubRes.Id), fi, settings.Sources[source].GetClearApiKey());
return client.UploadFilesToServer(new Uri(pubRes.Id), fi, settings.Sources[source].GetClearApiKey(Program.rsa));
}
catch (HttpRequestException hrex)
catch (HttpRequestException httpEx)
{
var report = new PushReport
{
PkgName = fi.Name,
Message = "HttpRequest: " + hrex.Message,
StackTrace = hrex.StackTrace,
StatusCode = hrex.HResult.ToString()
Message = "HttpRequest: " + httpEx.Message,
StackTrace = httpEx.StackTrace,
StatusCode = httpEx.HResult.ToString()
};
Console.Error.WriteLine(hrex.Message);
Console.Error.WriteLine(httpEx.Message);
return report;
}
catch (Exception ex)

@ -14,11 +14,11 @@ namespace isn
List<PushReport> pushReports = new List<PushReport>();
var cmd = new PushCommand(Settings);
if (CurrentSource == null) throw new InvalidOperationException("source is null");
if (Settings.CurrentSource == null) throw new InvalidOperationException("source is null");
var source = Settings.CurrentSource;
foreach (string pkg in pkgs)
{
var report = cmd.Run(pkg, CurrentSource, apiKey);
var report = cmd.Run(pkg, source.Url, source.GetClearApiKey(rsa));
pushReports.Add(report);
}
return pushReports;

@ -14,7 +14,7 @@ namespace isn
var args = storeoptions.Parse(storeArgs);
if (args.Count != 1)
{
Console.Error.WriteLine("StoreApiKey command takse one and only one argument, the key.");
Console.Error.WriteLine("StoreApiKey command takes only one argument, the key.");
shouldShowPushHelp=true;
}
if (shouldShowPushHelp)
@ -25,7 +25,7 @@ namespace isn
}
else
{
Settings.Sources[Settings.CurrentSourceKey].SetApiKey(args[0]);
Settings.Sources[Settings.CurrentSourceKey].SetApiKey(Program.rsa, args[0]);
SaveConfig();
}
}

@ -1,13 +1,6 @@
using System.Data.Common;
using System.Linq;
using System.Threading.Tasks;
using isnd.Services;
using isnd.Entities;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using NuGet.Versioning;
using isnd.Data.Packages.Catalog;
using isnd.Data.Catalog;
using isn.abst;
namespace isnd.Controllers
@ -23,6 +16,7 @@ namespace isnd.Controllers
}
[HttpGet("~" + Constants.ApiVersionPrefix + ApiConfig.Content + "/{id}/{version}.json")]
[HttpGet("~" + Constants.ApiVersionPrefix + ApiConfig.Registration + "/{id}/{version}.json")]
public async Task<IActionResult> CatalogRegistration(string id, string version)
{

@ -7,52 +7,51 @@ using isn.abst;
namespace isnd.Controllers
{
// TODO /search GET {@id}?q={QUERY}&skip={SKIP}&take={TAKE}&prerelease={PRERELEASE}&semVerLevel={SEMVERLEVEL}&packageType={PACKAGETYPE}
public partial class PackagesController
{
// Web get the paquet
[HttpGet("~" + Constants.ApiVersionPrefix + ApiConfig.Nuget + "/{id}/{lower}/{idf}-{lowerf}."
[HttpGet("~" + Constants.ApiVersionPrefix + ApiConfig.Nuget + "/{id}/{lower}/{idf}-{lowerFromName}."
+ Constants.PacketFileExtension)]
[HttpGet("~" + Constants.ApiVersionPrefix + ApiConfig.Content + "/{id}/{lower}/{idf}-{lowerf}."
[HttpGet("~" + Constants.ApiVersionPrefix + ApiConfig.Content + "/{id}/{lower}/{idf}-{lowerFromName}."
+ Constants.PacketFileExtension)]
public IActionResult GetPackage(
[FromRoute][SafeName][Required] string id,
[FromRoute][SafeName][Required] string lower,
[FromRoute] string idf, [FromRoute] string lowerf)
[FromRoute] string idf, [FromRoute] string lowerFromName)
{
var pkgpath = Path.Combine(isndSettings.PackagesRootDir,
var pkgPath = Path.Combine(isndSettings.PackagesRootDir,
id, lower, $"{id}-{lower}." + Constants.PacketFileExtension
);
FileInfo pkgfi = new FileInfo(pkgpath);
FileInfo pkgFileInfo = new FileInfo(pkgPath);
if (!pkgfi.Exists)
if (!pkgFileInfo.Exists)
{
return BadRequest("!pkgfi.Exists");
return BadRequest("!pkgFileInfo.Exists");
}
return File(pkgfi.OpenRead(), "application/zip; charset=binary");
return File(pkgFileInfo.OpenRead(), "application/zip; charset=binary");
}
// Web get spec
[HttpGet("~" + Constants.ApiVersionPrefix + ApiConfig.Nuspec + "/{id}/{lower}/{idf}-{lowerf}."
[HttpGet("~" + Constants.ApiVersionPrefix + ApiConfig.Nuspec + "/{id}/{lower}/{idf}-{lowerFromName}."
+ Constants.SpecFileExtension)]
public IActionResult GetNuspec(
[FromRoute][SafeName][Required] string id,
[FromRoute][SafeName][Required] string lower,
[FromRoute][SafeName][Required] string idf,
[FromRoute][SafeName][Required] string lowerf)
[FromRoute][SafeName][Required] string lowerFromName)
{
var pkgpath = Path.Combine(isndSettings.PackagesRootDir,
var pkgPath = Path.Combine(isndSettings.PackagesRootDir,
id, lower, $"{id}." + Constants.SpecFileExtension);
FileInfo pkgfi = new FileInfo(pkgpath);
if (!pkgfi.Exists)
FileInfo pkgFileInfo = new FileInfo(pkgPath);
if (!pkgFileInfo.Exists)
{
return BadRequest("!pkgfi.Exists");
return BadRequest("!pkgFileInfo.Exists");
}
return File(pkgfi.OpenRead(), "text/xml; charset=utf-8");
return File(pkgFileInfo.OpenRead(), "text/xml; charset=utf-8");
}
}
}

@ -1,13 +1,12 @@
using Microsoft.AspNetCore.Mvc;
using NuGet.Versioning;
using isnd.Entities;
using isn.abst;
namespace isnd.Controllers
{
public partial class PackagesController
{
[HttpGet("~" + Constants.ApiVersionPrefix + ApiConfig.Find)]
[HttpGet("~" + ApiConfig.V2Find)]
public IActionResult GetVersions(
string id,
string lower,

@ -1,25 +1,13 @@
using Microsoft.VisualBasic.CompilerServices;
using System.Linq.Expressions;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using NuGet.Packaging.Core;
using NuGet.Versioning;
using isnd.Data;
using isnd.Helpers;
using isnd.Entities;
using Microsoft.AspNetCore.Http;
using isn.abst;
using isnd.Data.Packages;
using Microsoft.EntityFrameworkCore;
namespace isnd.Controllers
{
@ -38,184 +26,26 @@ namespace isnd.Controllers
var files = new List<string>();
ViewData["files"] = files;
var clearkey = protector.Unprotect(apiKey);
var apikey = dbContext.ApiKeys.SingleOrDefault(k => k.Id == clearkey);
if (apikey == null)
var clearKey = protector.Unprotect(apiKey);
var dbApiKey = dbContext.ApiKeys.SingleOrDefault(k => k.Id == clearKey);
if (dbApiKey == null)
{
logger.LogError("403 : no api-key");
return Unauthorized();
}
Commit commit = new Commit
{
Action = PackageAction.PublishPackage,
TimeStamp = DateTimeOffset.Now.ToUniversalTime()
};
foreach (IFormFile file in Request.Form.Files)
{
string initpath = Path.Combine(Environment.GetEnvironmentVariable("TEMP") ??
Environment.GetEnvironmentVariable("TMP") ?? "/tmp",
$"isn-{Guid.NewGuid()}."+Constants.PacketFileExtension);
using (FileStream fw = new FileStream(initpath, FileMode.Create))
{
file.CopyTo(fw);
}
using (FileStream fw = new FileStream(initpath, FileMode.Open))
{
var archive = new ZipArchive(fw);
var spec = archive.Entries.FirstOrDefault(e => e.FullName.EndsWith("." + Constants.SpecFileExtension));
if (spec == null) return BadRequest(new { error = "no " + Constants.SpecFileExtension + " from archive" });
string pkgpath;
NuGetVersion version;
string pkgid;
string fullpath;
using (var specstr = spec.Open())
{
NuspecCoreReader reader = new NuspecCoreReader(specstr);
string pkgdesc = reader.GetDescription();
var types = reader.GetPackageTypes();
pkgid = reader.GetId();
version = reader.GetVersion();
string pkgidpath = Path.Combine(isndSettings.PackagesRootDir,
pkgid);
pkgpath = Path.Combine(pkgidpath, version.ToFullString());
string name = $"{pkgid}-{version}."+Constants.PacketFileExtension;
fullpath = Path.Combine(pkgpath, name);
var destpkgiddir = new DirectoryInfo(pkgidpath);
Package pkg = dbContext.Packages.SingleOrDefault(p => p.Id == pkgid);
if (pkg != null)
{
if (pkg.OwnerId != apikey.UserId)
{
return new ForbidResult();
}
pkg.Description = pkgdesc;
}
else
{
pkg = new Package
{
Id = pkgid,
Description = pkgdesc,
OwnerId = apikey.UserId,
LatestCommit = commit
};
dbContext.Packages.Add(pkg);
}
if (!destpkgiddir.Exists) destpkgiddir.Create();
var source = new FileInfo(initpath);
var dest = new FileInfo(fullpath);
var destdir = new DirectoryInfo(dest.DirectoryName);
if (dest.Exists)
{
logger.LogWarning($"Existant package on disk : '{dest.FullName}'");
// La version existe sur le disque,
// mais si elle ne l'est pas en base de donnéés,
// on remplace la version sur disque.
string exFullString = version.ToFullString();
var pkgv = dbContext.PackageVersions.
Include(v=>v.LatestCommit)
.SingleOrDefault(
v => v.PackageId == pkg.Id && v.FullString == exFullString
);
if (pkgv!=null && ! pkgv.IsDeleted)
{
string msg = $"existant : {pkg.Id}-{exFullString}";
logger.LogWarning("400 : {msg}", msg);
ModelState.AddModelError("pkgversion", msg);
return BadRequest(this.CreateAPIKO("existant"));
} else dest.Delete();
}
{
if (!destdir.Exists) destdir.Create();
source.MoveTo(fullpath);
files.Add(name);
string fullstringversion = version.ToFullString();
var pkgvers = dbContext.PackageVersions.Where
(v => v.PackageId == pkg.Id && v.FullString == fullstringversion);
if (pkgvers.Count() > 0)
{
foreach (var v in pkgvers.ToArray())
dbContext.PackageVersions.Remove(v);
}
// FIXME default type or null
if (types==null || types.Count==0)
dbContext.PackageVersions.Add
(new PackageVersion{
Package = pkg,
Major = version.Major,
Minor = version.Minor,
Patch = version.Patch,
Revision = version.Revision,
IsPrerelease = version.IsPrerelease,
FullString = version.ToFullString(),
Type = null,
LatestCommit = commit
});
else
foreach (var type in types)
{
var pkgver = new PackageVersion
{
Package = pkg,
Major = version.Major,
Minor = version.Minor,
Patch = version.Patch,
IsPrerelease = version.IsPrerelease,
FullString = version.ToFullString(),
Type = type.Name,
LatestCommit = commit
};
dbContext.PackageVersions.Add(pkgver);
}
dbContext.Commits.Add(commit);
await dbContext.SaveChangesAsync();
await packageManager.ÛpdateCatalogForAsync(commit);
logger.LogInformation($"new paquet : {spec.Name}");
}
}
using (var shacrypto = System.Security.Cryptography.SHA512.Create())
{
using (var stream = System.IO.File.OpenRead(fullpath))
{
var hash = shacrypto.ComputeHash(stream);
var shafullname = fullpath + ".sha512";
var hashtext = Convert.ToBase64String(hash);
var hashtextbytes = Encoding.ASCII.GetBytes(hashtext);
using (var shafile = System.IO.File.OpenWrite(shafullname))
{
shafile.Write(hashtextbytes, 0, hashtextbytes.Length);
}
}
}
string nuspecfullpath = Path.Combine(pkgpath, pkgid + "." + Constants.SpecFileExtension);
FileInfo nfpi = new(nuspecfullpath);
if (nfpi.Exists)
nfpi.Delete();
spec.ExtractToFile(nuspecfullpath);
}
var version = await packageManager.PutPackageAsync(file.OpenReadStream(), dbApiKey.UserId);
logger.LogInformation($"new package : {version.PackageId} {version.NugetLink}");
}
return Ok();
}
catch (Exception ex)
{
logger.LogError("PUT exception : " + ex.Message);
var message = $"PUT exception : {ex.Message} ({ex.GetType().Name})";
logger.LogError(message);
logger.LogError("Stack Trace : " + ex.StackTrace);
return new ObjectResult(new { ViewData, ex.Message })
return new ObjectResult(new { ViewData, message })
{ StatusCode = 500 };
}
}

@ -13,6 +13,7 @@ namespace isnd.Controllers
// Web get the paquet
[HttpGet("~" + Constants.ApiVersionPrefix + ApiConfig.Search)]
[HttpHead("~" + Constants.ApiVersionPrefix + ApiConfig.Search)]
[HttpHead("~" + ApiConfig.V2Find)]
public async Task<IActionResult> Search(
string q=null,
int skip=0,

@ -24,6 +24,11 @@ namespace isnd.Data
.HasKey( v => new { v.PackageId, v.FullString } );
_ = builder.Entity<PackageVersion>()
.HasOne(v => v.Package).WithMany(p => p.Versions).HasForeignKey(x => x.PackageId);
_ = builder.Entity<PackageDependencyGroup>().HasOne(g=>g.PackageVersion)
.WithMany(v => v.DependencyGroups).HasForeignKey(x => new { x.PackageId, x.PackageVersionFullString } );
}
@ -50,5 +55,8 @@ namespace isnd.Data
/// </summary>
/// <value></value>
public DbSet<Commit> Commits { get; set; }
public DbSet<Dependency> Dependencies { get; set; }
public DbSet<PackageDependencyGroup> PackageDependencyGroups { get; set; }
}
}

@ -1,6 +0,0 @@
namespace isnd.Data.Catalog
{
public class DependencyGroup
{
}
}

@ -33,6 +33,14 @@ namespace isnd.Data.Catalog
CommitId = pkg.CommitId;
CommitTimeStamp = pkg.LatestCommit.CommitTimeStamp;
IsListed = !pkg.IsDeleted && pkg.Package.Public;
if (pkg.DependencyGroups!=null)
{
if (pkg.DependencyGroups.Count>0)
{
dependencyGroups = pkg.DependencyGroups.ToArray();
}
}
// TODO Licence Project Urls, Summary, Title, etc ...
}
@ -56,7 +64,7 @@ namespace isnd.Data.Catalog
/// The dependencies of the package, grouped by target framework
/// </summary>
/// <value>array of objects</value>
public DependencyGroup[] dependencyGroups { get; set; } = Array.Empty<DependencyGroup>();
public PackageDependencyGroup[] dependencyGroups { get; set; }
/// <summary>
/// The deprecation associated with the package
@ -127,8 +135,8 @@ namespace isnd.Data.Catalog
[JsonProperty("id")]
public string PackageId { get; set; }
public IEnumerable<PackageDependencyGroup> DependencySets { get; set; }
= Array.Empty<PackageDependencyGroup>();
public IEnumerable<NuGet.Packaging.PackageDependencyGroup> DependencySets { get; set; }
= Array.Empty<NuGet.Packaging.PackageDependencyGroup>();
public long? DownloadCount { get; set; }

@ -0,0 +1,31 @@
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using System.ComponentModel.DataAnnotations.Schema;
using isnd.Data.Packages;
namespace isnd.Data
{
public class Dependency
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Id { get;set;}
/// <summary>
/// Package Id
/// </summary> <summary>
///
/// </summary>
/// <value></value>
[Required]
[ForeignKey("Group")]
public string DependencyGroupId { set ; get ; }
public virtual PackageDependencyGroup Group{ get; set; }
public string Version { get; set; }
public string Exclude { get; set; }
}
}

@ -0,0 +1,35 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.DotNet.Scaffolding.Shared.ProjectModel;
using Microsoft.Identity.Client;
using Newtonsoft.Json;
using NuGet.Frameworks;
using NuGet.Packaging;
using NuGet.Packaging.Core;
using NuGet.Versioning;
namespace isnd.Data
{
public class PackageDependencyGroup
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Id { get ; set;}
[Required]
public string PackageId { get ; set;}
[Required]
public string PackageVersionFullString { get ; set;}
[JsonProperty("targetFramework")]
public string TargetFramework { get; set; }
[JsonProperty("dependencies")]
[ForeignKey("DependencyGroupId")]
public virtual List<Dependency> Dependencies { get; set; }
public virtual PackageVersion PackageVersion { get; set; }
}
}

@ -11,6 +11,10 @@ using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using NuGet.Versioning;
using System;
using NuGet.Packaging;
using System.Collections.Generic;
using Package = isnd.Data.Packages.Package;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
namespace isnd.Data
{
@ -60,6 +64,8 @@ namespace isnd.Data
public string CommitId { get => CommitNId.ToString(); }
public virtual Commit LatestCommit { get; set; }
public virtual List<PackageDependencyGroup> DependencyGroups { get; set; }
public string NugetLink => $"{ApiConfig.Nuget}/{PackageId}/{FullString}/{PackageId}-{FullString}."
+ Constants.PacketFileExtension;
public string NuspecLink => $"{ApiConfig.Nuspec}/{PackageId}/{FullString}/{PackageId}-{FullString}."
@ -73,6 +79,7 @@ namespace isnd.Data
return new Catalog.Package(apiBase, this.PackageId , FullString,
new Catalog.PackageDetails(this, apiBase, apiBase + ApiConfig.Registration + "/" + this.PackageId + "/" + FullString + ".json"));
}
public bool IsDeleted => LatestCommit?.Action == PackageAction.DeletePackage;
}
}

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using isn.Abstract;
using isnd.Data;
@ -13,11 +14,11 @@ namespace isnd.Interfaces
public interface IPackageManager
{
string CatalogBaseUrl { get; }
AutoCompleteResult AutoComplete(string pkgid, int skip, int take, bool prerelease = false, string packageType = null);
AutoCompleteResult AutoComplete(string pkgid, int skip=0, int take=25, bool prerelease = false, string packageType = null);
string[] GetVersions(string pkgid, NuGetVersion parsedVersion, bool prerelease = false, string packageType = null, int skip = 0, int take = 25);
IEnumerable<Resource> GetResources();
Task<PackageRegistration> ÛpdateCatalogForAsync(Commit commit);
Task<PackageRegistration> UpdateCatalogForAsync(Commit commit);
Task<PackageDeletionReport> DeletePackageAsync(string pkgid, string version, string type);
Task<PackageDeletionReport> UserAskForPackageDeletionAsync(string userid, string pkgId, string lower, string type);
Task<PackageVersion> GetPackageAsync(string pkgid, string version, string type);
@ -28,6 +29,8 @@ namespace isnd.Interfaces
Task<PackageRegistration> GetPackageRegistrationIndexAsync(PackageRegistrationQuery query);
Task<PackageSearchResult> SearchPackageAsync(PackageRegistrationQuery query);
Task<PackageVersion> PutPackageAsync(Stream packageStream, string ownerId);
}
}

@ -0,0 +1,529 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using isnd.Data;
#nullable disable
namespace isnd.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20240318003218_dependencies")]
partial class dependencies
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.3")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("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<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("ProviderKey")
.HasColumnType("text");
b.Property<string>("ProviderDisplayName")
.HasColumnType("text");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("text");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("text");
b.Property<string>("RoleId")
.HasColumnType("text");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("text");
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<string>("Value")
.HasColumnType("text");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("isnd.Data.ApiKeys.ApiKey", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("text");
b.Property<DateTimeOffset>("CreationDate")
.HasColumnType("timestamp with time zone");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("text");
b.Property<int>("ValidityPeriodInDays")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("ApiKeys");
});
modelBuilder.Entity("isnd.Data.ApplicationUser", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<int>("AccessFailedCount")
.HasColumnType("integer");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("boolean");
b.Property<string>("FullName")
.HasColumnType("text");
b.Property<bool>("LockoutEnabled")
.HasColumnType("boolean");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("timestamp with time zone");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("PasswordHash")
.HasColumnType("text");
b.Property<string>("PhoneNumber")
.HasColumnType("text");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("boolean");
b.Property<string>("SecurityStamp")
.HasColumnType("text");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("boolean");
b.Property<string>("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);
});
modelBuilder.Entity("isnd.Data.Dependency", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("text");
b.Property<string>("DependencyGroupId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Exclude")
.HasColumnType("text");
b.Property<string>("Version")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("DependencyGroupId");
b.ToTable("Depedencies");
});
modelBuilder.Entity("isnd.Data.PackageDependencyGroup", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("text");
b.Property<string>("PackageVersionFullString")
.HasColumnType("character varying(256)");
b.Property<string>("PackageVersionId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PackageVersionPackageId")
.HasColumnType("character varying(1024)");
b.Property<string>("TargetFramework")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("PackageVersionPackageId", "PackageVersionFullString");
b.ToTable("PackageDependencyGroups");
});
modelBuilder.Entity("isnd.Data.PackageVersion", b =>
{
b.Property<string>("PackageId")
.HasMaxLength(1024)
.HasColumnType("character varying(1024)");
b.Property<string>("FullString")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<long>("CommitNId")
.HasColumnType("bigint");
b.Property<bool>("IsPrerelease")
.HasColumnType("boolean");
b.Property<int>("Major")
.HasColumnType("integer");
b.Property<int>("Minor")
.HasColumnType("integer");
b.Property<int>("Patch")
.HasColumnType("integer");
b.Property<int>("Revision")
.HasColumnType("integer");
b.Property<string>("Type")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("PackageId", "FullString");
b.HasIndex("CommitNId");
b.ToTable("PackageVersions");
});
modelBuilder.Entity("isnd.Data.Packages.Commit", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
b.Property<int>("Action")
.HasColumnType("integer");
b.Property<DateTimeOffset>("TimeStamp")
.HasColumnType("timestamp with time zone");
b.HasKey("Id");
b.ToTable("Commits");
});
modelBuilder.Entity("isnd.Data.Packages.Package", b =>
{
b.Property<string>("Id")
.HasMaxLength(1024)
.HasColumnType("character varying(1024)");
b.Property<long>("CommitNId")
.HasColumnType("bigint");
b.Property<string>("Description")
.HasMaxLength(1024)
.HasColumnType("character varying(1024)");
b.Property<string>("OwnerId")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("Public")
.HasColumnType("boolean");
b.HasKey("Id");
b.HasIndex("CommitNId");
b.HasIndex("OwnerId");
b.ToTable("Packages");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("isnd.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("isnd.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("isnd.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("isnd.Data.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("isnd.Data.ApiKeys.ApiKey", b =>
{
b.HasOne("isnd.Data.ApplicationUser", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("isnd.Data.Dependency", b =>
{
b.HasOne("isnd.Data.PackageDependencyGroup", "Group")
.WithMany("Dependencies")
.HasForeignKey("DependencyGroupId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Group");
});
modelBuilder.Entity("isnd.Data.PackageDependencyGroup", b =>
{
b.HasOne("isnd.Data.PackageVersion", null)
.WithMany("DependencyGroups")
.HasForeignKey("PackageVersionPackageId", "PackageVersionFullString");
});
modelBuilder.Entity("isnd.Data.PackageVersion", b =>
{
b.HasOne("isnd.Data.Packages.Commit", "LatestCommit")
.WithMany("Versions")
.HasForeignKey("CommitNId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("isnd.Data.Packages.Package", "Package")
.WithMany("Versions")
.HasForeignKey("PackageId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("LatestCommit");
b.Navigation("Package");
});
modelBuilder.Entity("isnd.Data.Packages.Package", b =>
{
b.HasOne("isnd.Data.Packages.Commit", "LatestCommit")
.WithMany()
.HasForeignKey("CommitNId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("isnd.Data.ApplicationUser", "Owner")
.WithMany()
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("LatestCommit");
b.Navigation("Owner");
});
modelBuilder.Entity("isnd.Data.PackageDependencyGroup", b =>
{
b.Navigation("Dependencies");
});
modelBuilder.Entity("isnd.Data.PackageVersion", b =>
{
b.Navigation("DependencyGroups");
});
modelBuilder.Entity("isnd.Data.Packages.Commit", b =>
{
b.Navigation("Versions");
});
modelBuilder.Entity("isnd.Data.Packages.Package", b =>
{
b.Navigation("Versions");
});
#pragma warning restore 612, 618
}
}
}

@ -0,0 +1,74 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace isnd.Migrations
{
/// <inheritdoc />
public partial class dependencies : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "PackageDependencyGroups",
columns: table => new
{
Id = table.Column<string>(type: "text", nullable: false),
PackageVersionId = table.Column<string>(type: "text", nullable: false),
TargetFramework = table.Column<string>(type: "text", nullable: true),
PackageVersionFullString = table.Column<string>(type: "character varying(256)", nullable: true),
PackageVersionPackageId = table.Column<string>(type: "character varying(1024)", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_PackageDependencyGroups", x => x.Id);
table.ForeignKey(
name: "FK_PackageDependencyGroups_PackageVersions_PackageVersionPacka~",
columns: x => new { x.PackageVersionPackageId, x.PackageVersionFullString },
principalTable: "PackageVersions",
principalColumns: new[] { "PackageId", "FullString" });
});
migrationBuilder.CreateTable(
name: "Depedencies",
columns: table => new
{
Id = table.Column<string>(type: "text", nullable: false),
DependencyGroupId = table.Column<string>(type: "text", nullable: false),
Version = table.Column<string>(type: "text", nullable: true),
Exclude = table.Column<string>(type: "text", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Depedencies", x => x.Id);
table.ForeignKey(
name: "FK_Depedencies_PackageDependencyGroups_DependencyGroupId",
column: x => x.DependencyGroupId,
principalTable: "PackageDependencyGroups",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Depedencies_DependencyGroupId",
table: "Depedencies",
column: "DependencyGroupId");
migrationBuilder.CreateIndex(
name: "IX_PackageDependencyGroups_PackageVersionPackageId_PackageVers~",
table: "PackageDependencyGroups",
columns: new[] { "PackageVersionPackageId", "PackageVersionFullString" });
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Depedencies");
migrationBuilder.DropTable(
name: "PackageDependencyGroups");
}
}
}

@ -17,7 +17,7 @@ namespace isnd.Migrations
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "7.0.4")
.HasAnnotation("ProductVersion", "8.0.3")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
@ -247,6 +247,55 @@ namespace isnd.Migrations
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("isnd.Data.Dependency", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("text");
b.Property<string>("DependencyGroupId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Exclude")
.HasColumnType("text");
b.Property<string>("Version")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("DependencyGroupId");
b.ToTable("Depedencies");
});
modelBuilder.Entity("isnd.Data.PackageDependencyGroup", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("text");
b.Property<string>("PackageVersionFullString")
.HasColumnType("character varying(256)");
b.Property<string>("PackageVersionId")
.IsRequired()
.HasColumnType("text");
b.Property<string>("PackageVersionPackageId")
.HasColumnType("character varying(1024)");
b.Property<string>("TargetFramework")
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("PackageVersionPackageId", "PackageVersionFullString");
b.ToTable("PackageDependencyGroups");
});
modelBuilder.Entity("isnd.Data.PackageVersion", b =>
{
b.Property<string>("PackageId")
@ -396,6 +445,24 @@ namespace isnd.Migrations
b.Navigation("User");
});
modelBuilder.Entity("isnd.Data.Dependency", b =>
{
b.HasOne("isnd.Data.PackageDependencyGroup", "Group")
.WithMany("Dependencies")
.HasForeignKey("DependencyGroupId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Group");
});
modelBuilder.Entity("isnd.Data.PackageDependencyGroup", b =>
{
b.HasOne("isnd.Data.PackageVersion", null)
.WithMany("DependencyGroups")
.HasForeignKey("PackageVersionPackageId", "PackageVersionFullString");
});
modelBuilder.Entity("isnd.Data.PackageVersion", b =>
{
b.HasOne("isnd.Data.Packages.Commit", "LatestCommit")
@ -434,6 +501,16 @@ namespace isnd.Migrations
b.Navigation("Owner");
});
modelBuilder.Entity("isnd.Data.PackageDependencyGroup", b =>
{
b.Navigation("Dependencies");
});
modelBuilder.Entity("isnd.Data.PackageVersion", b =>
{
b.Navigation("DependencyGroups");
});
modelBuilder.Entity("isnd.Data.Packages.Commit", b =>
{
b.Navigation("Versions");

@ -0,0 +1,25 @@
using System;
using System.Runtime.Serialization;
namespace isnd.Services
{
[Serializable]
internal class InvalidPackageException : InvalidOperationException
{
public InvalidPackageException()
{
}
public InvalidPackageException(string message) : base(message)
{
}
public InvalidPackageException(string message, Exception innerException) : base(message, innerException)
{
}
protected InvalidPackageException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}

@ -1,18 +1,26 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using isn.abst;
using isn.Abstract;
using isnd.Data;
using isnd.Data.Catalog;
using isnd.Data.Packages;
using isnd.Entities;
using isnd.Helpers;
using isnd.Interfaces;
using isnd.ViewModels;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using NuGet.Packaging.Core;
using NuGet.Versioning;
using System.Xml;
using System.Xml.Linq;
using System.Threading;
namespace isnd.Services
{
@ -31,7 +39,7 @@ namespace isnd.Services
public IEnumerable<Resource> GetResources()
{
var res = new List<Resource>
{
new Resource(apiBase + ApiConfig.Package,
@ -46,7 +54,7 @@ namespace isnd.Services
{
Comment = "URI template used by NuGet Client to construct details URL for packages"
},
new Resource(apiBase + ApiConfig.Content,
"PackageBaseAddress/3.0.0")
{
@ -61,11 +69,26 @@ namespace isnd.Services
Comment = "Auto complete service"
},
new Resource(apiBase + ApiConfig.Search,
"SearchQueryService")
{
Comment = "Query endpoint of NuGet Search service (primary) used by RC clients"
},
new Resource(apiBase + ApiConfig.Search,
"SearchQueryService/3.0.0-beta")
{
Comment = "Query endpoint of NuGet Search service (primary) used by RC clients"
},
new Resource(apiBase + ApiConfig.Search,
"SearchQueryService/3.0.0-rc")
{
Comment = "Query endpoint of NuGet Search service (primary) used by RC clients"
},
new Resource(apiBase + ApiConfig.Search,
"SearchQueryService/3.5.0")
{
Comment = "Query endpoint of NuGet Search service (primary) used by RC clients"
},
new Resource(apiBase + ApiConfig.Registration,
"RegistrationsBaseUrl/Versioned")
@ -107,7 +130,7 @@ namespace isnd.Services
v => v.PackageId == id
&& (prerelease || !v.IsPrerelease)
&& (packageType == null || v.Type == packageType)
&& (parsedVersion==null || parsedVersion.CompareTo
&& (parsedVersion == null || parsedVersion.CompareTo
(new SemanticVersion(v.Major, v.Minor, v.Patch)) < 0)
)
.OrderBy(v => v.NugetVersion)
@ -115,8 +138,6 @@ namespace isnd.Services
.Skip(skip).Take(take).ToArray();
}
public string CatalogBaseUrl => apiBase;
private IsndSettings isndSettings;
@ -124,17 +145,17 @@ namespace isnd.Services
public virtual async Task<PackageRegistration> GetCatalogIndexAsync()
{
return await ÛpdateCatalogForAsync(null);
return await UpdateCatalogForAsync(null);
}
public async Task<PackageRegistration> ÛpdateCatalogForAsync
public async Task<PackageRegistration> UpdateCatalogForAsync
(Commit reason = null)
{
int i = 0;
string baseid = apiBase + ApiConfig.Catalog;
string bidreg = $"{apiBase}{ApiConfig.Registration}";
PackageRegistration index = new PackageRegistration(baseid);
string baseId = apiBase + ApiConfig.Catalog;
string baseRegistrationId = $"{apiBase}{ApiConfig.Registration}";
PackageRegistration index = new PackageRegistration(baseId);
var scope = await dbContext.Commits.OrderBy(c => c.TimeStamp).ToArrayAsync();
@ -149,21 +170,21 @@ namespace isnd.Services
.Include(pkg => pkg.LatestCommit)
.ToArrayAsync())
.GroupBy((q) => q.Id);
foreach (var pkgid in validPkgs)
foreach (var pkgIdGroup in validPkgs)
{
CatalogPage page = index.Items.FirstOrDefault
(p => p.GetPackageId() == pkgid.Key);
(p => p.GetPackageId() == pkgIdGroup.Key);
if (page == null)
{
page = new CatalogPage(pkgid.Key, apiBase);
page = new CatalogPage(pkgIdGroup.Key, apiBase);
index.Items.Add(page);
}
foreach (var pkgv in pkgid)
foreach (var package in pkgIdGroup)
{
page.AddVersionRange(pkgv.Versions);
page.AddVersionRange(package.Versions);
}
}
reason = commit;
@ -173,7 +194,7 @@ namespace isnd.Services
}
public async Task<PackageDeletionReport> DeletePackageAsync
(string pkgid, string version, string type)
(string pkgId, string version, string type)
{
// TODO deletion on disk
var commit = new Commit
@ -183,7 +204,7 @@ namespace isnd.Services
};
dbContext.Commits.Add(commit);
var pkg = await dbContext.PackageVersions.SingleOrDefaultAsync(
v => v.PackageId == pkgid &&
v => v.PackageId == pkgId &&
v.FullString == version &&
v.Type == type
);
@ -193,15 +214,15 @@ namespace isnd.Services
}
dbContext.PackageVersions.Remove(pkg);
await dbContext.SaveChangesAsync();
await ÛpdateCatalogForAsync(commit);
await UpdateCatalogForAsync(commit);
return new PackageDeletionReport { Deleted = true, DeletedVersion = pkg };
}
public async Task<PackageVersion> GetPackageAsync
(string pkgid, string version, string type)
(string pkgId, string version, string type)
{
return await dbContext.PackageVersions.SingleOrDefaultAsync(
v => v.PackageId == pkgid &&
v => v.PackageId == pkgId &&
v.FullString == version &&
v.Type == type
);
@ -215,9 +236,9 @@ namespace isnd.Services
.Include(v => v.Package.LatestCommit)
.Include(v => v.Package.Owner)
.Include(v => v.LatestCommit)
.Where(v => v.PackageId == pkgId
&& v.FullString == semver
&& v.LatestCommit !=null
.Where(v => v.PackageId == pkgId
&& v.FullString == semver
&& v.LatestCommit != null
).SingleOrDefaultAsync()).ToPackage(
apiBase);
}
@ -238,12 +259,13 @@ namespace isnd.Services
public IEnumerable<Data.Catalog.Package> SearchCatalogEntriesById
(string pkgId, string semver, string pkgType, bool preRelease)
{
// PackageDependency
return dbContext.PackageVersions
.Include(v => v.Package)
.Include(v => v.Package.Owner)
.Include(v => v.Package.LatestCommit)
.Include(v => v.LatestCommit)
.Include(v => v.DependencyGroups)
.Where(v => v.PackageId == pkgId && semver == v.FullString
&& (pkgType == null || pkgType == v.Type)
&& (preRelease || !v.IsPrerelease))
@ -271,21 +293,21 @@ namespace isnd.Services
.Include(p => p.Owner)
.Include(p => p.LatestCommit)
.SingleOrDefaultAsync(p => p.Id.ToLower() == query.Query);
if (scope==null) return null;
if (scope.Versions.Count==0) return null;
if (scope == null) return null;
if (scope.Versions.Count == 0) return null;
string bid = $"{apiBase}{ApiConfig.Registration}";
foreach (var version in scope.Versions)
version.LatestCommit = dbContext.Commits.Single(c=>c.Id == version.CommitNId);
version.LatestCommit = dbContext.Commits.Single(c => c.Id == version.CommitNId);
return
new PackageRegistration(apiBase, scope);
}
public async Task<PackageSearchResult> SearchPackageAsync(PackageRegistrationQuery query)
{
string bid = $"{apiBase}{ApiConfig.Registration}";
if (string.IsNullOrWhiteSpace(query.Query))
query.Query="";
var scope = dbContext.Packages
if (string.IsNullOrWhiteSpace(query.Query))
query.Query = "";
var scope = dbContext.Packages
.Include(p => p.Owner)
.Include(p => p.Versions)
.Include(p => p.LatestCommit)
@ -293,11 +315,155 @@ namespace isnd.Services
.Where(p => p.Id.StartsWith(query.Query)
&& p.LatestCommit != null
&& (query.Prerelease || p.Versions.Any(p => !p.IsPrerelease))
&& p.Versions.Count()>0)
&& p.Versions.Count() > 0)
.OrderBy(p => p.CommitNId);
return new PackageSearchResult(await scope.Skip(query.Skip).Take(query.Take)
.ToListAsync(), apiBase, scope.Count());
}
public async Task<PackageVersion> PutPackageAsync(Stream packageStream, string ownerId)
{
PackageVersion version = null;
using (packageStream)
{
using (var archive = new ZipArchive(packageStream))
{
var spec = archive.Entries.FirstOrDefault(e => e.FullName.EndsWith("." + Constants.SpecFileExtension));
if (spec == null) throw new InvalidPackageException("no " + Constants.SpecFileExtension + " from archive");
string pkgPath;
NuGetVersion nugetVersion;
string pkgId;
string fullPath;
using var specificationStream = spec.Open();
using XmlReader xmlReader = XmlReader.Create(specificationStream);
var xMeta = XElement.Load(xmlReader, LoadOptions.None).Descendants().First();
string packageDescription = xMeta.Descendants().FirstOrDefault(x => x.Name.LocalName == "description")?.Value;
var frameWorks = xMeta
.Descendants().FirstOrDefault(x => x.Name.LocalName =="frameworkReferences")
.Descendants().Where(x => x.Name.LocalName =="group")
.Select(x=> x.Attribute("targetFramework").Value).ToArray();
var types = "Package";
pkgId = xMeta.Descendants().FirstOrDefault(x => x.Name.LocalName =="id")?.Value;
string pkgVersion = xMeta.Descendants().FirstOrDefault(x => x.Name.LocalName =="version")?.Value;
if (!NuGetVersion.TryParse(pkgVersion, out nugetVersion))
throw new InvalidPackageException("metadata/version");
var frameworkReferences = frameWorks.Select(g => new PackageDependencyGroup
{
TargetFramework = g
}
).ToList();
string packageIdPath = Path.Combine(isndSettings.PackagesRootDir,
pkgId);
pkgPath = Path.Combine(packageIdPath, nugetVersion.ToFullString());
string name = $"{pkgId}-{nugetVersion}." + Constants.PacketFileExtension;
fullPath = Path.Combine(pkgPath, name);
var packageIdPathInfo = new DirectoryInfo(packageIdPath);
Data.Packages.Package pkg = dbContext.Packages.SingleOrDefault(p => p.Id == pkgId);
Commit commit = new Commit
{
Action = PackageAction.PublishPackage,
TimeStamp = DateTimeOffset.Now.ToUniversalTime()
};
if (pkg != null)
{
// Update
pkg.Description = packageDescription;
pkg.LatestCommit = commit;
}
else
{
// First version
pkg = new Data.Packages.Package
{
Id = pkgId,
Description = packageDescription,
OwnerId = ownerId,
LatestCommit = commit
};
dbContext.Packages.Add(pkg);
}
// here, the package is or new, or owned by the key owner
if (!packageIdPathInfo.Exists) packageIdPathInfo.Create();
var dest = new FileInfo(fullPath);
var destDir = new DirectoryInfo(dest.DirectoryName);
if (dest.Exists) dest.Delete();
if (!destDir.Exists) destDir.Create();
packageStream.Seek(0, SeekOrigin.Begin);
using (var fileStream = File.Create(fullPath))
{
await packageStream.CopyToAsync(fileStream);
}
string fullStringVersion = nugetVersion.ToFullString();
var pkgVersions = dbContext.PackageVersions.Where
(v => v.PackageId == pkg.Id && v.FullString == fullStringVersion);
if (pkgVersions.Count() > 0)
{
foreach (var v in pkgVersions.ToArray())
dbContext.PackageVersions.Remove(v);
}
// FIXME default type or null
dbContext.PackageVersions.Add
(version = new PackageVersion
{
Package = pkg,
Major = nugetVersion.Major,
Minor = nugetVersion.Minor,
Patch = nugetVersion.Patch,
Revision = nugetVersion.Revision,
IsPrerelease = nugetVersion.IsPrerelease,
FullString = nugetVersion.ToFullString(),
Type =types,
LatestCommit = commit
});
dbContext.Commits.Add(commit);
await dbContext.SaveChangesAsync();
version.DependencyGroups = frameworkReferences;
await UpdateCatalogForAsync(commit);
using (var shaCrypto = System.Security.Cryptography.SHA512.Create())
{
using (var stream = System.IO.File.OpenRead(fullPath))
{
var hash = shaCrypto.ComputeHash(stream);
var shaFullName = fullPath + ".sha512";
var hashText = Convert.ToBase64String(hash);
var hashTextBytes = Encoding.ASCII.GetBytes(hashText);
using (var shaFile = System.IO.File.OpenWrite(shaFullName))
{
shaFile.Write(hashTextBytes, 0, hashTextBytes.Length);
}
}
}
string nugetSpecificationFullPath = Path.Combine(pkgPath, pkgId + "." + Constants.SpecFileExtension);
FileInfo nugetSpecificationFullPathInfo = new(nugetSpecificationFullPath);
if (nugetSpecificationFullPathInfo.Exists)
nugetSpecificationFullPathInfo.Delete();
spec.ExtractToFile(nugetSpecificationFullPath);
}
}
return version;
}
}
}

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<UserSecretsId>85fd766d-5d23-4476-aed1-463b2942e86a</UserSecretsId>
<IsPackable>true</IsPackable>
<PackageLicenseExpression>WTFPL</PackageLicenseExpression>
@ -13,31 +13,30 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.15" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="7.0.15" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.15">
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="8.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.15" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="7.0.11" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.3" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.2" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="7.0.15" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.11" IncludeAssets="All" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.15" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.15" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.3" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.2" IncludeAssets="All" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.3" />
<PackageReference Include="NuGet.Packaging.Core" Version="6.8.0" />
<PackageReference Include="MailKit" Version="4.3.0" />
<PackageReference Include="unleash.client" Version="4.1.6" />
<PackageReference Include="NuGet.Packaging.Core" Version="6.9.1" />
<PackageReference Include="MailKit" Version="4.4.0" />
<PackageReference Include="GitVersion.MsBuild" Version="5.12.0">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="System.Security.Cryptography.Pkcs" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Antiforgery" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="7.0.15" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../isn.abst/isn.abst.csproj" />

@ -2,3 +2,9 @@ https://api.nuget.org/v3/registration5-gz-semver2/yavsc.abstract/index.json
https://api.nuget.org/v3/catalog0/data/2019.09.17.10.18.17/yavsc.abstract.1.0.6-rc07.json
http://localhost:5000/v3/index
http://localhost:5000/v3/registration/isn.abst/index.json
https://isn.pschneider.fr/v3/registration5-gz-semver2/isn.abst/index.json
https://isn.pschneider.fr/v3/content/isn.abst/index.json
http://localhost:5000/v3/registration5-gz-semver2/isn.abst/index.json
http://localhost:5000/v3/registration/isn.abst/index.json
http://localhost:5000/v3/content/isn.abst/index.json

@ -1,12 +1,12 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using isn.Abstract;
using System.Linq;
using Xunit;
using isn.abst;
using System.Security.Cryptography;
namespace isn.tests
{
@ -42,6 +42,18 @@ namespace isn.tests
var pub = model.Resources.FirstOrDefault((r) => r.Type.StartsWith("PackagePublish/"));
Assert.True(pub != null);
}
[Fact]
public void TestSetApiKey()
{
string source = "http://localhost:3002/v3/index.json";
var setting = Settings.Create();
setting.Sources[source] = new SourceSettings{ Url=source };
string testingKey = "CfDJ8LF3SbIJ4FJAgs7uIQKhdCAYCNVXRwU6TEoaXOo1_ZpG2u8TCGFP2z13hw9xR0LC0gdbr1QGwNndiXUl4DI74nxyBi-T1oC33PWtE-5vgiJWeCH223PYtoSEdzDiWovwJZWJbQON0WqoG8vSfbrBXTmicD6oxF4ghwXXexY0RiRR";
var rsa = RSA.Create(setting.RSAParameters);
setting.Sources[source].SetApiKey(rsa,testingKey);
Assert.Equal(testingKey, setting.Sources[source].GetClearApiKey(rsa));
}
}
}

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net7.0</TargetFrameworks>
<TargetFrameworks>net8.0</TargetFrameworks>
<IsPackable>false</IsPackable>
<NoWarn>NETSDK1138</NoWarn>
<AssemblyVersion>1.0.7.0</AssemblyVersion>
@ -9,12 +9,13 @@
<Version>1.0.7</Version>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="xunit" Version="2.7.0" />
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
<PackageReference Include="xunit.runner.reporters" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.2" />
<PackageToolReference Include="xunit.runner.console" Version="2.4.2" PrivateAssets="All" />
<PackageReference Include="xunit.runner.reporters" Version="2.7.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.7" />
<PackageToolReference Include="xunit.runner.console" Version="2.5.7" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\isn\isn.csproj" />

@ -151,10 +151,9 @@ namespace isnd.host.tests
{
ILogger logger = new TestLogger();
CancellationToken cancellationToken = CancellationToken.None;
SourceRepository repository = Repository.Factory.GetCoreV3(SPIIndexURI);
repository.PackageSource.AllowInsecureConnections=true;
PackageSearchResource resource = await repository.GetResourceAsync<PackageSearchResource>();
SearchFilter searchFilter = new SearchFilter(includePrerelease: true);
@ -184,6 +183,15 @@ namespace isnd.host.tests
await pushRes.Push(new List<string>{ "../../../../../src/isnd/bin/Release/isnd.1.1.4.nupkg" }, null,
5000, false, GetApiKey, GetSymbolsApiKey, false, false, symbolPackageResource, logger);
}
[Fact]
public void TestDepedency()
{
PackageDependencyGroup g = new PackageDependencyGroup
{
TargetFramework="net7.0"
};
}
private string GetSymbolsApiKey(string apiUrl)
{

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFrameworks>net7.0</TargetFrameworks>
<TargetFrameworks>net8.0</TargetFrameworks>
<IsPackable>false</IsPackable>
<UserSecretsId>d7144e46-4e63-4391-ba86-64b61f6e7be4</UserSecretsId>
<NoWarn>NETSDK1138</NoWarn>
@ -12,10 +12,10 @@
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="XunitXml.TestLogger" Version="3.1.20" />
<PackageReference Include="xunit" Version="2.6.6" />
<PackageReference Include="xunit" Version="2.7.0" />
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
<PackageReference Include="xunit.runner.reporters" Version="2.6.6" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6" />
<PackageReference Include="xunit.runner.reporters" Version="2.7.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.7" />
<PackageToolReference Include="xunit.runner.console" Version="2.4.2" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>

Loading…