Compare commits

49 Commits

Author SHA1 Message Date
301a276a7b Find package versions ignoring character case 2025-03-04 14:28:29 +00:00
bc1464db7f Web search result fixed 2025-02-01 19:44:31 +00:00
913f2ae5dd Layout 2024-11-03 16:20:43 +00:00
27ad3841a0 Fixes he layout 2024-11-03 16:10:03 +00:00
a6f9d974d3 Licence 2024-11-03 13:30:21 +00:00
a3d3e51259 uodated : usefull 2024-11-03 11:12:48 +00:00
753f54dad0 Home index 2024-11-02 15:49:26 +00:00
72ecf30458 repo cleanup 2024-10-20 19:54:36 +01:00
5f1dca3d61 dotnet package resolution 2024-10-20 19:47:49 +01:00
c615886a4c unit testing 2024-10-19 13:25:58 +01:00
2b45e421dd GetMetaData OK 2024-10-09 23:19:38 +01:00
7ca465d4a9 JQuery version bump 2024-07-28 14:07:06 +01:00
8532b81638 WIP package resolution 2024-07-19 10:21:25 +02:00
01405abdf2 Package deletion and types 2024-04-28 15:44:12 +01:00
62a8bf27cb Error Page 2024-04-28 15:43:10 +01:00
9c9ee6cc4c trying with isn.abst 2024-04-22 21:51:06 +01:00
3af05fc034 dependencies 2024-04-16 22:30:39 +01:00
04a467dd83 registration and detail url's 2024-03-31 15:59:43 +01:00
96f91c3ba0 net-8, localization, privacy, and js ref's 2024-03-30 09:45:17 +00:00
aeecc524a4 WIP /search 2024-03-24 16:53:12 +00:00
e229439174 Fixes the registration page index 2024-03-16 14:33:10 +00:00
c6c52a48c4 some details 2024-03-11 20:55:52 +00:00
8656bce151 fixes the catalog page 2024-03-11 01:02:11 +00:00
d6180aa154 REORG 2024-03-10 20:10:05 +00:00
89e1b5a235 Testing the push method (TODO coherence between return code a physical result) 2024-03-09 21:43:56 +00:00
e69462999e Cx strings contains user secrets ... 2024-03-09 16:13:30 +00:00
e1a012e15b orthograph 2024-02-12 02:45:47 +00:00
16258d1c33 version bumps 2024-02-12 02:22:10 +00:00
019d858ac2 fixes the chown 2024-02-12 02:21:50 +00:00
72839e8251 Release ! 2023-05-09 23:44:25 +01:00
7987fa04a0 update 2023-05-07 20:05:10 +01:00
9e79edc624 Trying and find a package from API 2023-05-01 22:05:39 +01:00
06b0115405 Commit Id and TimerStamps 2023-04-30 23:40:39 +01:00
cea05fe8ff Package Version constraints 2023-04-30 20:25:03 +01:00
fa6272ac28 cleaner code 2023-04-26 18:22:48 +01:00
77a16c5551 avoid a null ref 2023-04-26 18:22:39 +01:00
e2775dedaf Searching deleted package 2023-04-26 01:22:33 +01:00
e5932a11c2 cleanup 2023-04-26 01:22:00 +01:00
4de566ee41 Cleanup 2023-04-26 01:21:23 +01:00
d3b96088ab a better result 2023-04-25 22:40:58 +01:00
56e4b6c526 gives the store-api-key some options 2023-04-18 22:07:14 +01:00
b20e822427 ignore current source setting, at serialisation 2023-04-18 22:06:44 +01:00
9fc3f7ea4a MAssive attack 2023-04-18 08:43:22 +01:00
9363cd756b makes "store-api-ey" a command 2023-04-17 19:04:59 +01:00
9e2cebde2e This "push" is only for testing purposes 2023-04-17 19:04:10 +01:00
a2e22c357a Get a failed path 2023-04-17 19:03:11 +01:00
ee636a29ac db upgrade 2023-04-17 01:33:03 +01:00
d3f49a3c01 make packs 2023-04-17 01:32:47 +01:00
58d1f26701 No release yet 2023-04-17 01:20:42 +01:00
1894 changed files with 295579 additions and 20514 deletions

24
.build Normal file
View File

@ -0,0 +1,24 @@
<?xml version="1.0"?>
<project name="Isn" default="clean" basedir=".">
<description>The Hello World of build files.</description>
<property name="debug" value="true" overwrite="false" />
<target name="clean" description="remove all generated files">
<delete dir="src/isn.abst/bin/" failonerror="false" />
<delete dir="src/isn.abst/obj/" failonerror="false" />
<delete dir="src/isn/bin/" failonerror="false" />
<delete dir="src/isn/obj/" failonerror="false" />
<delete dir="src/isnd/bin/" failonerror="false" />
<delete dir="src/isnd/obj/" failonerror="false" />
<delete dir="test/isn.tests/bin/" failonerror="false" />
<delete dir="test/isn.tests/obj/" failonerror="false" />
<delete dir="test/isnd.tests/bin/" failonerror="false" />
<delete dir="test/isnd.tests/obj/" failonerror="false" />
</target>
<target name="build" description="build all">
<exec program="dotnet" commandline="build" />
</target>
<target name="test" description="test all">
<exec program="dotnet" commandline="test" />
</target>
</project>

24
.gitignore vendored
View File

@ -1,3 +1,21 @@
/packages/
/bin/
/obj/
.sass-cache/
/src/isnd/bin/
/src/isnd/obj/
/src/isn/obj
/src/isn/bin
/src/isn/.vscode/
/test/isnd.tests/obj/
/test/isnd.tests/bin/
/test/isn.tests/bin
/test/isn.tests/obj/
/src/isn.abst/bin
/src/isn.abst/obj
/src/isnd/packages/
/test/data/test-isn/bin/
/test/data/test-isn/obj
.fake
/artifacts/
/.vs/
/.vscode/
appsettings.Development.json

66
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,66 @@
# You can override the included template(s) by including variable overrides
# See https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
# Note that environment variables can be set in several places
# See https://docs.gitlab.com/ee/ci/variables/#priority-of-environment-variables
image: busybox:latest
before_script:
- dotnet restore
- dotnet nuget remove source gitlab || true
nonreg:
tags:
- dotnet
stage: test
environment: Development
script:
- |
echo "setting : $ISND_TESTING_SETTINGS"
dotnet build
cat $ISND_TESTING_SETTINGS > test/isnd.tests/appsettings.json
dotnet test
publish:
tags:
- dotnet
stage: deploy
dependencies:
- nonreg
artifacts:
paths:
- src/isnd/bin/Release/netcoreapp2.1/publish/
when: always
script:
- dotnet publish --configuration Release
deploy-to-gitlab:
tags:
- dotnet
stage: deploy
dependencies:
- nonreg
artifacts:
paths:
- src/*/bin/Release/*.nupkg
when: always
script:
- dotnet pack -c Release
- dotnet nuget add source --name gitlab --username gitlab+deploy-token-2 --password
$CI_JOB_TOKEN --store-password-in-clear-text "$CI_SERVER_URL/api/v4/projects/$CI_PROJECT_ID/packages/nuget/index.json"
- dotnet nuget push src/*/bin/Release/*.nupkg -s gitlab
deploy-to-isn:
stage: deploy
tags:
- dotnet
artifacts:
paths:
- src/*/bin/Release/*.nupkg
when: always
script:
- dotnet pack --configuration Release --no-restore
- cd src/isn
- find -name "*.nupkg" -exec dotnet run push -s $ISNSOURCE -k $ISNAPIKEY {} \;
environment:
name: production
url: $ISNSOURCE
dependencies:
- nonreg
stages:
- test
- deploy

29
.vscode/launch.json vendored
View File

@ -1,29 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (web)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
"program": "${workspaceFolder}/bin/Debug/netcoreapp2.0/nuget-host.dll",
"args": [],
"cwd": "${workspaceFolder}",
"stopAtEntry": false,
"OS-COMMENT5": "Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser",
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\\\bNow listening on:\\\\s+(https?://\\\\S+)"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
}
]
}

63
.vscode/tasks.json vendored
View File

@ -7,11 +7,31 @@
"type": "process",
"args": [
"build",
"${workspaceFolder}/nuget-host.csproj",
"/p:Configuration=Debug",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
"problemMatcher": "$msCompile",
"group": "build"
},
{
"label": "db-upgrade",
"command": "dotnet",
"type": "process",
"args": [
"ef",
"database",
"update"
],
"problemMatcher": "$msCompile",
"options": {
"cwd": "${workspaceFolder}/src/isnd",
"env": {
"ASPNETCORE_ENV": "Development"
}
},
"dependsOn":["build"],
"group": "test"
},
{
"label": "publish",
@ -19,35 +39,52 @@
"type": "process",
"args": [
"publish",
"${workspaceFolder}/nuget-host.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
"/p:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary",
"/p:TargetFramework=netcoreapp2.1",
"/p:RuntimeIdentifier=linux-x64",
"/p:PublishDir=${workspaceFolder}/artifacts"
],
"problemMatcher": "$msCompile"
"problemMatcher": "$msCompile",
"options": {
"cwd": "${workspaceFolder}"
},
"group": "none"
},
{
"label": "launch-dotnet",
"label": "test",
"command": "dotnet",
"type": "process",
"options": {
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"args": [
"${workspaceFolder}/bin/Debug/netcoreapp2.0/nuget-host.dll"
"test",
"/property:GenerateFullPaths=true",
"--logger:xunit"
],
"problemMatcher": "$msCompile"
"problemMatcher": "$msCompile",
"dependsOn": [ "build"],
"group": "test"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"bin/Debug/netcoreapp2.0/nuget-host.dll",
"/property:GenerateFullPaths=true"
"bin/Debug/netcoreapp2.1/isnd.dll",
"/property:GenerateFullPaths=true",
"/restore"
],
"options": {
"env": {
"ASPNETCORE_ENV": "Development"
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"problemMatcher": "$msCompile"
"problemMatcher": "$msCompile",
"group": "none"
}
]
}

View File

@ -1,45 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using nuget_host.Models;
namespace nuget_host.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
public IActionResult About()
{
ViewData["Message"] = "Your application description page.";
return View();
}
public IActionResult Contact()
{
ViewData["Message"] = "Your contact page.";
return View();
}
public IActionResult Test()
{
ViewData["Message"] = "Your contact page.";
return Ok(ViewData);
}
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
}

View File

@ -1,113 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using NuGet.Packaging;
namespace nuget_host.Controllers
{
public class PackagesController : Controller
{
private ILogger<PackagesController> logger;
public PackagesController(ILoggerFactory loggerFactory)
{
logger = loggerFactory.CreateLogger<PackagesController>();
}
[HttpPut("packages/{*spec}")]
public IActionResult Put(string spec)
{
string path = null;
if (string.IsNullOrEmpty(spec))
{
var clientVersionId = Request.Headers["X-NuGet-Client-Version"];
ViewData["nuget client "] = "nuget {clientVersionId}";
foreach (var file in Request.Form.Files)
{
string initpath = "package.nupkg";
using (FileStream fw = new FileStream(initpath, FileMode.Create))
{
file.CopyTo(fw);
}
using (FileStream fw = new FileStream(initpath, FileMode.Open))
{
var archive = new System.IO.Compression.ZipArchive(fw);
foreach (var filename in archive.GetFiles())
{
if (filename.EndsWith(".nuspec"))
{
// var entry = archive.GetEntry(filename);
var specstr = archive.OpenFile(filename);
NuspecReader reader = new NuspecReader(specstr);
string pkgdesc = reader.GetDescription();
string pkgid = reader.GetId();
var version = reader.GetVersion();
path = Path.Combine(Startup.SourceDir,
Path.Combine(pkgid,
Path.Combine(version.ToFullString(),
$"{pkgid}-{version}.nupkg")));
var source = new FileInfo(initpath);
var dest = new FileInfo(path);
var destdir = new DirectoryInfo(dest.DirectoryName);
if (dest.Exists)
return BadRequest(new {error = "existant"});
if (!destdir.Exists) destdir.Create();
source.MoveTo(path);
}
}
}
}
}
else
{
ViewData["spec"] = spec;
// TODO Assert valid sem ver spec
var filelst = new DirectoryInfo(Startup.SourceDir);
var fi = new FileInfo(spec);
var lst = filelst.GetFiles(fi.Name + "*.nupkg");
ViewData["lst"] = lst.Select(entry => entry.Name);
}
return Ok(ViewData);
}
[HttpGet("Packages/{spec}")]
public IActionResult Index(string spec)
{
if (string.IsNullOrEmpty(spec))
{
ViewData["warn"] = "no spec";
/*
usr/lib/mono/msbuild/Current/bin/NuGet.targets(128,5): error : Failed to retrieve information about 'Microsoft.VisualStudio.Web.CodeGeneration.Tools' from remote source 'http://localhost:5000/Packages/FindPackagesById()?id='Microsoft.VisualStudio.Web.CodeGeneration.Tools'&semVerLevel=2.0.0'. [/home/paul/workspace/nuget-host/nuget-host.csproj]
*/
}
else
{
ViewData["spec"] = spec;
// TODO Assert valid sem ver spec
var filelst = new DirectoryInfo(Startup.SourceDir);
var fi = new FileInfo(spec);
var lst = filelst.GetDirectories(spec);
ViewData["lst"] = lst.Select(entry => entry.Name);
}
return Ok(ViewData);
}
}
}

14
LICENSE Normal file
View File

@ -0,0 +1,14 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

46
Makefile Normal file
View File

@ -0,0 +1,46 @@
CONFIGURATION=Debug
TARGETFV=net8.0
all: build-isn build-isnd
build-%: src/%
dotnet build -p:Configuration=$(CONFIGURATION) $^
pack-%: src/%
dotnet pack $^
watch:
dotnet watch --project=src/isnd
test-push:
isn push src/isn/bin/Debug/isn.*.nupkg
clean-%: src/%
rm -rf $^/bin $^/obj
packs: pack-isn pack-isnd pack-isn.abst
clean: clean-isnd clean-isn clean-isn.abst
TARGETFRAMEWORK=net8.0
server-update:
dotnet build -c Release src/isnd
dotnet publish -c Release -f $(TARGETFRAMEWORK) src/isnd
sudo systemctl stop isnd
sudo cp -a src/isnd/bin/Release/$(TARGETFRAMEWORK)/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/$(TARGETFRAMEWORK)/* /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

View File

@ -1,11 +0,0 @@
using System;
namespace nuget_host.Models
{
public class ErrorViewModel
{
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
}
}

View File

@ -1,9 +1,100 @@
# Usage
# ISN
Hébergement de paquets logiciels Nuget, fournissant une implémentation minimale de l'API Nuget V3
## Usage
````sh
nuget sources remove -Name dev
nuget sources add -Name dev -Source Http://localhost:5000/packages
nuget push your-versionned.nupkg -ApiKey 15d0dda1-4028-4896-9f1a-188817da23f4 -Source http://localhost:5000/packages
dotnet user-secrets set ConnectionStrings:DefaultConnection "Server=<lame-pgserver>;Port=<lame-pgport>;Database=<lame-dbname>;Username=<lame-dbusername>;Password=<lame-dbpass>;"
isnd&
# get an api-key from <http://localhost:5000/ApkKeys>
isn push -k <lame-api-key> -s http://localhost:5000/index.json your-lame-versionned.nupkg
wget http://localhost:5000/package/index.json?q=your&prerelease=true&semVerLevel=2.0.0
````
## Installation
### Compilation
Dans le dossier de la solution, compiler la solution :
````bash
dotnet build /restore -c Release
dotnet publish -c Release src/isnd
````
### Déploiement du serveur
La livraison initiale, aujourd'hui :
````bash
sudo mkdir -p /srv/www/isnd
sudo cp -a src/isnd/bin/Release/net8.0/publish/* /srv/www/isnd
sudo cp contrib/isnd.service /etc/systemd/system
chown -R isn:isn /srv/www/isnd/
sudo systemctl daemon-reload
````
Une base de donées Postgresql est requise, avec, pour faire simple,
son utilisateur, et le droit de créer des tables (ce dernier droit pourrait expirer, mais gare aux mises à jour).
Il faudra éditer la configuration pour indiquer :
* dans /etc/systemd/system/isnd.service , la connextion à une base de donnée Postresgql, sous la forme :
`"Server=<pgserver>;Port=<pgport>;Database=<dbname>;Username=<dbusername>;Password=<dbpass>;"`
* dans /srv/www/isnd/appsettings.Production.json, la connection au serveur de messagerie,
* l'URL externe du ou des sites à propulser, et à utiliser dans la description de service,
* et les autres détails.
Pour faire ceci, vous pourrez éditer une copie du fichier `appsettings.json` vers `appsettings.Production.json`,
pour renseigner toutes les valeurs spécifiées.
* Démarrer le serveur :
````bash
sudo systemctl start isnd
````
* Activation du serveur :
````bash
sudo systemctl enable isnd
````
### Installation du client
````bash
sudo mkdir /usr/local/lib/isn
sudo cp -a src/isn/bin/Release/net6.0/* /usr/local/lib/isn
sudo chown -R root.root /usr/local/lib/isn
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
dotnet publish -c Release -f net8.0 src/isnd
# MAJ du serveur
sudo systemctl stop isnd
sudo cp -a src/isnd/bin/Release/net8.0/publish/* /srv/www/isnd
sudo systemctl start isnd
# MAJ du client
sudo cp -a src/isn/bin/Release/net8.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
````

View File

@ -1,54 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace nuget_host
{
public class Startup
{
public Startup(IHostingEnvironment env, IConfiguration config)
{
Configuration = config;
}
public IConfiguration Configuration { get; }
public static string ExternalUrl { get; private set; }
public static string SourceDir { get; private set; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
ExternalUrl = Configuration["NuGet:ExternalUrl"];
SourceDir = Configuration["NuGet:SourceDir"];
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}

View File

@ -1,106 +0,0 @@
@{
ViewData["Title"] = "Home Page";
}
<div id="myCarousel" class="carousel slide" data-ride="carousel" data-interval="6000">
<ol class="carousel-indicators">
<li data-target="#myCarousel" data-slide-to="0" class="active"></li>
<li data-target="#myCarousel" data-slide-to="1"></li>
<li data-target="#myCarousel" data-slide-to="2"></li>
<li data-target="#myCarousel" data-slide-to="3"></li>
</ol>
<div class="carousel-inner" role="listbox">
<div class="item active">
<img src="~/images/banner1.svg" alt="ASP.NET" class="img-responsive" />
<div class="carousel-caption" role="option">
<p>
Learn how to build ASP.NET apps that can run anywhere.
<a class="btn btn-default" href="https://go.microsoft.com/fwlink/?LinkID=525028&clcid=0x409">
Learn More
</a>
</p>
</div>
</div>
<div class="item">
<img src="~/images/banner2.svg" alt="Visual Studio" class="img-responsive" />
<div class="carousel-caption" role="option">
<p>
There are powerful new features in Visual Studio for building modern web apps.
<a class="btn btn-default" href="https://go.microsoft.com/fwlink/?LinkID=525030&clcid=0x409">
Learn More
</a>
</p>
</div>
</div>
<div class="item">
<img src="~/images/banner3.svg" alt="Package Management" class="img-responsive" />
<div class="carousel-caption" role="option">
<p>
Bring in libraries from NuGet and npm, and automate tasks using Grunt or Gulp.
<a class="btn btn-default" href="https://go.microsoft.com/fwlink/?LinkID=525029&clcid=0x409">
Learn More
</a>
</p>
</div>
</div>
<div class="item">
<img src="~/images/banner4.svg" alt="Microsoft Azure" class="img-responsive" />
<div class="carousel-caption" role="option">
<p>
Learn how Microsoft's Azure cloud platform allows you to build, deploy, and scale web apps.
<a class="btn btn-default" href="https://go.microsoft.com/fwlink/?LinkID=525027&clcid=0x409">
Learn More
</a>
</p>
</div>
</div>
</div>
<a class="left carousel-control" href="#myCarousel" role="button" data-slide="prev">
<span class="glyphicon glyphicon-chevron-left" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
</a>
<a class="right carousel-control" href="#myCarousel" role="button" data-slide="next">
<span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span>
<span class="sr-only">Next</span>
</a>
</div>
<div class="row">
<div class="col-md-3">
<h2>Application uses</h2>
<ul>
<li>Sample pages using ASP.NET Core MVC</li>
<li>Theming using <a href="https://go.microsoft.com/fwlink/?LinkID=398939">Bootstrap</a></li>
</ul>
</div>
<div class="col-md-3">
<h2>How to</h2>
<ul>
<li><a href="https://go.microsoft.com/fwlink/?LinkID=398600">Add a Controller and View</a></li>
<li><a href="https://go.microsoft.com/fwlink/?LinkId=699315">Manage User Secrets using Secret Manager.</a></li>
<li><a href="https://go.microsoft.com/fwlink/?LinkId=699316">Use logging to log a message.</a></li>
<li><a href="https://go.microsoft.com/fwlink/?LinkId=699317">Add packages using NuGet.</a></li>
<li><a href="https://go.microsoft.com/fwlink/?LinkId=699319">Target development, staging or production environment.</a></li>
</ul>
</div>
<div class="col-md-3">
<h2>Overview</h2>
<ul>
<li><a href="https://go.microsoft.com/fwlink/?LinkId=518008">Conceptual overview of what is ASP.NET Core</a></li>
<li><a href="https://go.microsoft.com/fwlink/?LinkId=699320">Fundamentals of ASP.NET Core such as Startup and middleware.</a></li>
<li><a href="https://go.microsoft.com/fwlink/?LinkId=398602">Working with Data</a></li>
<li><a href="https://go.microsoft.com/fwlink/?LinkId=398603">Security</a></li>
<li><a href="https://go.microsoft.com/fwlink/?LinkID=699321">Client side development</a></li>
<li><a href="https://go.microsoft.com/fwlink/?LinkID=699322">Develop on different platforms</a></li>
<li><a href="https://go.microsoft.com/fwlink/?LinkID=699323">Read more on the documentation site</a></li>
</ul>
</div>
<div class="col-md-3">
<h2>Run &amp; Deploy</h2>
<ul>
<li><a href="https://go.microsoft.com/fwlink/?LinkID=517851">Run your app</a></li>
<li><a href="https://go.microsoft.com/fwlink/?LinkID=517853">Run tools such as EF migrations and more</a></li>
<li><a href="https://go.microsoft.com/fwlink/?LinkID=398609">Publish to Microsoft Azure Web Apps</a></li>
</ul>
</div>
</div>

View File

@ -1,22 +0,0 @@
@model ErrorViewModel
@{
ViewData["Title"] = "Error";
}
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}
<h3>Development Mode</h3>
<p>
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
</p>
<p>
<strong>Development environment should not be enabled in deployed applications</strong>, as it can result in sensitive information from exceptions being displayed to end users. For local debugging, development environment can be enabled by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>, and restarting the application.
</p>

View File

@ -1,71 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - nuget_host</title>
<environment include="Development">
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="~/css/site.css" />
</environment>
<environment exclude="Development">
<link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css"
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
</environment>
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a asp-area="" asp-controller="Home" asp-action="Index" class="navbar-brand">nuget_host</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a asp-area="" asp-controller="Home" asp-action="Index">Home</a></li>
<li><a asp-area="" asp-controller="Home" asp-action="About">About</a></li>
<li><a asp-area="" asp-controller="Home" asp-action="Contact">Contact</a></li>
</ul>
</div>
</div>
</nav>
<div class="container body-content">
@RenderBody()
<hr />
<footer>
<p>&copy; 2018 - nuget_host</p>
</footer>
</div>
<environment include="Development">
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
</environment>
<environment exclude="Development">
<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.2.0.min.js"
asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
asp-fallback-test="window.jQuery"
crossorigin="anonymous"
integrity="sha384-K+ctZQ+LL8q6tP7I94W+qzQsfRV2a+AfHIi9k8z8l9ggpc8X+Ytst4yBo/hH+8Fk">
</script>
<script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/bootstrap.min.js"
asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal"
crossorigin="anonymous"
integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa">
</script>
<script src="~/js/site.min.js" asp-append-version="true"></script>
</environment>
@RenderSection("Scripts", required: false)
</body>
</html>

View File

@ -1,3 +0,0 @@
@using nuget_host
@using nuget_host.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

View File

@ -1,6 +0,0 @@
{
"NuGet": {
"ExternalUrl" : "<http://localhost:5000/Packages",
"SourceDir" : "packages"
}
}

View File

@ -1,12 +0,0 @@
{
"NuGet": {
"ExternalUrl" : "<http://your-external.url",
"SourceDir" : "<your-Source-dir>"
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
}
}

41
dotnet-tools.json Normal file
View File

@ -0,0 +1,41 @@
{
"version": 1,
"isRoot": true,
"tools": {
"codecov.tool": {
"version": "1.13.0",
"commands": [
"codecov"
],
"rollForward": false
},
"gitversion.tool": {
"version": "5.10.1",
"commands": [
"dotnet-gitversion"
],
"rollForward": false
},
"gitreleasemanager.tool": {
"version": "0.13.0",
"commands": [
"dotnet-gitreleasemanager"
],
"rollForward": false
},
"Wyam2.Tool": {
"version": "3.0.0-rc3",
"commands": [
"wyam2"
],
"rollForward": false
},
"dotnet-ef": {
"version": "8.0.7",
"commands": [
"dotnet-ef"
],
"rollForward": false
}
}
}

27
gitversion.json Normal file
View File

@ -0,0 +1,27 @@
{
"AssemblySemFileVer": "1.1.1.0",
"AssemblySemVer": "1.1.1.0",
"BranchName": "main",
"BuildMetaData": null,
"CommitDate": "2024-10-09",
"CommitsSinceVersionSource": 19,
"EscapedBranchName": "main",
"FullBuildMetaData": "Branch.main.Sha.2b45e421dda285b73bf7eb3387915030cd751b88",
"FullSemVer": "1.1.1-19",
"InformationalVersion": "1.1.1-19+Branch.main.Sha.2b45e421dda285b73bf7eb3387915030cd751b88",
"Major": 1,
"MajorMinorPatch": "1.1.1",
"Minor": 1,
"Patch": 1,
"PreReleaseLabel": "",
"PreReleaseLabelWithDash": "",
"PreReleaseNumber": 19,
"PreReleaseTag": "19",
"PreReleaseTagWithDash": "-19",
"SemVer": "1.1.1-19",
"Sha": "2b45e421dda285b73bf7eb3387915030cd751b88",
"ShortSha": "2b45e42",
"UncommittedChanges": 20,
"VersionSourceSha": "72839e8251297935a4ab138a99152733f07a23f2",
"WeightedPreReleaseNumber": 55019
}

101
isn.sln Normal file
View File

@ -0,0 +1,101 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.6.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{3C312E42-9A47-4BED-8265-A405FCA6AFFF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "isnd.tests", "test\isnd.tests\isnd.tests.csproj", "{9D758F00-17FF-433D-B088-F9C2D97C9BD1}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E8A2DF68-847A-4D88-B002-64FB666F696C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "isnd", "src\isnd\isnd.csproj", "{468DB0E4-6221-4E01-BEFF-F452865E59C1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "isn", "src\isn\isn.csproj", "{910E800A-59AE-46C4-B7C7-879986179246}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "isn.tests", "test\isn.tests\isn.tests.csproj", "{305F640E-11BA-44F9-95E0-C6882E9CD151}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "isn.abst", "src\isn.abst\isn.abst.csproj", "{4EFA7D3E-8B31-4BF7-96D1-B9F2867735C7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9D758F00-17FF-433D-B088-F9C2D97C9BD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9D758F00-17FF-433D-B088-F9C2D97C9BD1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9D758F00-17FF-433D-B088-F9C2D97C9BD1}.Debug|x64.ActiveCfg = Debug|Any CPU
{9D758F00-17FF-433D-B088-F9C2D97C9BD1}.Debug|x64.Build.0 = Debug|Any CPU
{9D758F00-17FF-433D-B088-F9C2D97C9BD1}.Debug|x86.ActiveCfg = Debug|Any CPU
{9D758F00-17FF-433D-B088-F9C2D97C9BD1}.Debug|x86.Build.0 = Debug|Any CPU
{9D758F00-17FF-433D-B088-F9C2D97C9BD1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9D758F00-17FF-433D-B088-F9C2D97C9BD1}.Release|Any CPU.Build.0 = Release|Any CPU
{9D758F00-17FF-433D-B088-F9C2D97C9BD1}.Release|x64.ActiveCfg = Release|Any CPU
{9D758F00-17FF-433D-B088-F9C2D97C9BD1}.Release|x64.Build.0 = Release|Any CPU
{9D758F00-17FF-433D-B088-F9C2D97C9BD1}.Release|x86.ActiveCfg = Release|Any CPU
{9D758F00-17FF-433D-B088-F9C2D97C9BD1}.Release|x86.Build.0 = Release|Any CPU
{468DB0E4-6221-4E01-BEFF-F452865E59C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{468DB0E4-6221-4E01-BEFF-F452865E59C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{468DB0E4-6221-4E01-BEFF-F452865E59C1}.Debug|x64.ActiveCfg = Debug|Any CPU
{468DB0E4-6221-4E01-BEFF-F452865E59C1}.Debug|x64.Build.0 = Debug|Any CPU
{468DB0E4-6221-4E01-BEFF-F452865E59C1}.Debug|x86.ActiveCfg = Debug|Any CPU
{468DB0E4-6221-4E01-BEFF-F452865E59C1}.Debug|x86.Build.0 = Debug|Any CPU
{468DB0E4-6221-4E01-BEFF-F452865E59C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{468DB0E4-6221-4E01-BEFF-F452865E59C1}.Release|Any CPU.Build.0 = Release|Any CPU
{468DB0E4-6221-4E01-BEFF-F452865E59C1}.Release|x64.ActiveCfg = Release|Any CPU
{468DB0E4-6221-4E01-BEFF-F452865E59C1}.Release|x64.Build.0 = Release|Any CPU
{468DB0E4-6221-4E01-BEFF-F452865E59C1}.Release|x86.ActiveCfg = Release|Any CPU
{468DB0E4-6221-4E01-BEFF-F452865E59C1}.Release|x86.Build.0 = Release|Any CPU
{910E800A-59AE-46C4-B7C7-879986179246}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{910E800A-59AE-46C4-B7C7-879986179246}.Debug|Any CPU.Build.0 = Debug|Any CPU
{910E800A-59AE-46C4-B7C7-879986179246}.Debug|x64.ActiveCfg = Debug|Any CPU
{910E800A-59AE-46C4-B7C7-879986179246}.Debug|x64.Build.0 = Debug|Any CPU
{910E800A-59AE-46C4-B7C7-879986179246}.Debug|x86.ActiveCfg = Debug|Any CPU
{910E800A-59AE-46C4-B7C7-879986179246}.Debug|x86.Build.0 = Debug|Any CPU
{910E800A-59AE-46C4-B7C7-879986179246}.Release|Any CPU.ActiveCfg = Release|Any CPU
{910E800A-59AE-46C4-B7C7-879986179246}.Release|Any CPU.Build.0 = Release|Any CPU
{910E800A-59AE-46C4-B7C7-879986179246}.Release|x64.ActiveCfg = Release|Any CPU
{910E800A-59AE-46C4-B7C7-879986179246}.Release|x64.Build.0 = Release|Any CPU
{910E800A-59AE-46C4-B7C7-879986179246}.Release|x86.ActiveCfg = Release|Any CPU
{910E800A-59AE-46C4-B7C7-879986179246}.Release|x86.Build.0 = Release|Any CPU
{305F640E-11BA-44F9-95E0-C6882E9CD151}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{305F640E-11BA-44F9-95E0-C6882E9CD151}.Debug|Any CPU.Build.0 = Debug|Any CPU
{305F640E-11BA-44F9-95E0-C6882E9CD151}.Debug|x64.ActiveCfg = Debug|Any CPU
{305F640E-11BA-44F9-95E0-C6882E9CD151}.Debug|x64.Build.0 = Debug|Any CPU
{305F640E-11BA-44F9-95E0-C6882E9CD151}.Debug|x86.ActiveCfg = Debug|Any CPU
{305F640E-11BA-44F9-95E0-C6882E9CD151}.Debug|x86.Build.0 = Debug|Any CPU
{305F640E-11BA-44F9-95E0-C6882E9CD151}.Release|Any CPU.ActiveCfg = Release|Any CPU
{305F640E-11BA-44F9-95E0-C6882E9CD151}.Release|Any CPU.Build.0 = Release|Any CPU
{305F640E-11BA-44F9-95E0-C6882E9CD151}.Release|x64.ActiveCfg = Release|Any CPU
{305F640E-11BA-44F9-95E0-C6882E9CD151}.Release|x64.Build.0 = Release|Any CPU
{305F640E-11BA-44F9-95E0-C6882E9CD151}.Release|x86.ActiveCfg = Release|Any CPU
{305F640E-11BA-44F9-95E0-C6882E9CD151}.Release|x86.Build.0 = Release|Any CPU
{4EFA7D3E-8B31-4BF7-96D1-B9F2867735C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4EFA7D3E-8B31-4BF7-96D1-B9F2867735C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4EFA7D3E-8B31-4BF7-96D1-B9F2867735C7}.Debug|x64.ActiveCfg = Debug|Any CPU
{4EFA7D3E-8B31-4BF7-96D1-B9F2867735C7}.Debug|x64.Build.0 = Debug|Any CPU
{4EFA7D3E-8B31-4BF7-96D1-B9F2867735C7}.Debug|x86.ActiveCfg = Debug|Any CPU
{4EFA7D3E-8B31-4BF7-96D1-B9F2867735C7}.Debug|x86.Build.0 = Debug|Any CPU
{4EFA7D3E-8B31-4BF7-96D1-B9F2867735C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4EFA7D3E-8B31-4BF7-96D1-B9F2867735C7}.Release|Any CPU.Build.0 = Release|Any CPU
{4EFA7D3E-8B31-4BF7-96D1-B9F2867735C7}.Release|x64.ActiveCfg = Release|Any CPU
{4EFA7D3E-8B31-4BF7-96D1-B9F2867735C7}.Release|x64.Build.0 = Release|Any CPU
{4EFA7D3E-8B31-4BF7-96D1-B9F2867735C7}.Release|x86.ActiveCfg = Release|Any CPU
{4EFA7D3E-8B31-4BF7-96D1-B9F2867735C7}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{9D758F00-17FF-433D-B088-F9C2D97C9BD1} = {3C312E42-9A47-4BED-8265-A405FCA6AFFF}
{468DB0E4-6221-4E01-BEFF-F452865E59C1} = {E8A2DF68-847A-4D88-B002-64FB666F696C}
{910E800A-59AE-46C4-B7C7-879986179246} = {E8A2DF68-847A-4D88-B002-64FB666F696C}
{305F640E-11BA-44F9-95E0-C6882E9CD151} = {3C312E42-9A47-4BED-8265-A405FCA6AFFF}
{4EFA7D3E-8B31-4BF7-96D1-B9F2867735C7} = {E8A2DF68-847A-4D88-B002-64FB666F696C}
EndGlobalSection
EndGlobal

View File

@ -1,20 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<UserSecretsId>85fd766d-5d23-4476-aed1-463b2942e86a</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.5" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Common" Version="1.0.0-alpha2-final" />
<PackageReference Include="NuGet.Packaging.Core" Version="5.9.0" />
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.2" />
</ItemGroup>
</Project>

35
src/.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,35 @@
{
"version": "0.2.0",
"configurations": [
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md.
"name": ".NET Core Launch (web)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/isnd/bin/Debug/net8.0/isnd.dll",
"args": [],
"cwd": "${workspaceFolder}/isnd",
"stopAtEntry": false,
// Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
"serverReadyAction": {
"action": "openExternally",
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
},
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
}
]
}

41
src/.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,41 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/isnd/isnd.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary;ForceNoAlign"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/isnd/isnd.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary;ForceNoAlign"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"--project",
"${workspaceFolder}/isnd/isnd.csproj"
],
"problemMatcher": "$msCompile"
}
]
}

11
src/Directory.Build.props Normal file
View File

@ -0,0 +1,11 @@
<Project>
<PropertyGroup>
<Version>1.0.8</Version>
<Authors>paul</Authors>
<Copyright>WFPL</Copyright>
<PackageLicenseExpression></PackageLicenseExpression>
<ProjectUrl></ProjectUrl>
<RepositoryUrl></RepositoryUrl>
<RepositoryType>git</RepositoryType>
</PropertyGroup>
</Project>

11
src/isn.abst/APIKO.cs Normal file
View File

@ -0,0 +1,11 @@
using System.Collections.Generic;
namespace isn
{
public class APIKO
{
public string Context { get; set; }
public Dictionary<string, string[]> Errors { get; set; }
}
}

24
src/isn.abst/ApiConfig.cs Normal file
View File

@ -0,0 +1,24 @@
using System;
using isn.abst;
namespace isnd.Entities
{
public static class ApiConfig
{
public const string Index = "/index.json";
public const string Catalog = "/catalog";
public const string Package = "/package";
public const string Search = "/search";
public const string AutoComplete = "/autocomplete";
public const string Registration = "/registration";
public const string ContentBase = "/content";
public const string Nuget = "/nuget";
public const string PackageDetailUriTemplate = Package+"/{id}/{version}";
[Obsolete("use the V3 search")]
public const string V2Find = "/v2/FindPackagesById()"; // /FindPackagesById()??$filter=IsLatestVersion&$orderby=Version desc&$top=1&id='isn.abst'
}
}

View File

@ -0,0 +1,18 @@
using isnd.Data.Catalog;
using Newtonsoft.Json;
namespace isn.Abstract
{
public class ApiIndexViewModel : Permalink
{
public ApiIndexViewModel(string id) : base(id, "ApiIndex")
{
}
[JsonProperty("version")]
public string Version { get; set; }
[JsonProperty("resources")]
public Resource[] Resources { get; set; }
}
}

View File

@ -0,0 +1,9 @@
namespace isn.abst
{
public static class Constants
{
public const string PacketFileExtension = "nupkg";
public const string SpecFileExtension = "nuspec";
public const string ApiVersionPrefix = "/v3";
}
}

48
src/isn.abst/Permalink.cs Normal file
View File

@ -0,0 +1,48 @@
using Newtonsoft.Json;
namespace isnd.Data.Catalog
{
public abstract class Permalink
{
public Permalink(string id)
{
Type = GetType().Name;
this.id = id;
}
public Permalink(string id, string type)
{
Type = type;
this.id = id;
}
[JsonProperty("@type")]
public virtual string Type { get; set; }
[JsonProperty("@id")]
public string Id { get => id; }
protected string id;
public string GetId() { return id; }
public override bool Equals(object obj)
{
if (obj!=null)
{
if (GetType().IsAssignableFrom(obj.GetType()))
{
var rpobj = (Permalink) obj;
return this.id == rpobj.id;
}
}
return base.Equals(obj);
}
public override int GetHashCode()
{
return id.GetHashCode();
}
}
}

18
src/isn.abst/Resource.cs Normal file
View File

@ -0,0 +1,18 @@
using isnd.Data.Catalog;
using Newtonsoft.Json;
namespace isn.Abstract
{
public class Resource : Permalink
{
public Resource(string id, string typename) : base(id)
{
Type = typename;
}
[JsonProperty("comment")]
public string Comment {get; set; }
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace isnd.Attributes
{
public class SafeNameAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
if (!(value is string))
return false;
string str = value as string;
if (str.Length>126) return false;
if (str.Any(c => !char.IsLetterOrDigit(c)
&& !"-_.".Contains(c))) return false;
return true;
}
}
}

View File

@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
</Project>

7
src/isn/Constants.cs Normal file
View File

@ -0,0 +1,7 @@
namespace isn
{
internal static class Constants
{
internal const string ClientVersion = "isn v1.0";
}
}

View File

@ -0,0 +1,8 @@
namespace isn
{
public class IsndErrorMessage
{
public int ecode { get; set; }
public string msg { get; set; }
}
}

212
src/isn/Program.cs Normal file
View File

@ -0,0 +1,212 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using Mono.Options;
using Newtonsoft.Json;
namespace isn
{
public partial class Program
{
public static void LoadConfig()
{
FileInfo cfgSettingIf = new FileInfo(_configFileName);
if (cfgSettingIf.Exists)
{
var json = File.ReadAllText(cfgSettingIf.FullName);
Settings = JsonConvert.DeserializeObject<Settings>(json);
CurrentSource = Settings.DefaultSourceKey;
}
if (Settings==null)
{
Settings= Settings.Create();
}
}
static readonly OptionSet storeoptions = new OptionSet {
{ "s|source=", "use source", val => Settings.CurrentSourceKey ??= val },
{ "h|help", "show this message and exit", h => shouldShowPushHelp = h != null },
};
private static readonly string _configFileName =
Path.Combine(
Path.Combine(Environment.GetFolderPath(
Environment.SpecialFolder.UserProfile), ".isn"),
"config.json")
;
public const string push = "push";
static readonly OptionSet options = new OptionSet {
{ "h|help", "show this message and exit", h => shouldShowHelp = h != null },
{ "v|version", "show soft version info and exit", h => shouldShowVersion = h != null }
};
static readonly OptionSet pushoptions = new OptionSet {
{ "k|api-key=", "use api key", val => Settings.CurrentSource.SetApiKey(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 {
{ "h|help", "show this message and exit", h => shouldShowSourceHelp = h != null },
};
static readonly OptionSet showOptions = new OptionSet {
{ "h|help", "show this message and exit", h => shouldShowSourceHelp = h != null },
};
private static bool shouldShowHelp;
private static bool shouldShowVersion;
private static bool shouldShowSourceHelp;
private static bool shouldShowPushHelp;
public static Settings 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")
{
Run = sargs => SourceList(sargs)
};
var showconfig = new Command("config")
{
Run = sargs => ShowConfig()
};
var srcadd = new Command("add")
{
Run = sargs => SourceAdd(sargs)
};
var srcsetdef = new Command("set-default")
{
Run = sargs => SetDefaultSource(sargs?.FirstOrDefault())
};
var showCommand = new Command("show")
{
Run = sargs =>
{
var showCommandSet = new CommandSet("show")
{
showconfig
};
var pargs = showOptions.Parse(sargs);
if (shouldShowSourceHelp)
{
// output the options
Console.WriteLine("Sources Options:");
showOptions.WriteOptionDescriptions(Console.Out);
return;
}
showCommandSet.Run(pargs);
}
};
var srcCmd = new Command("sources")
{
Run = sargs =>
{
var sourcesCmdSet = new CommandSet("sources")
{
srclst,
srcadd,
srcsetdef
};
var pargs = sourceoptions.Parse(sargs);
if (shouldShowSourceHelp)
{
// output the options
Console.WriteLine("Sources Options:");
sourceoptions.WriteOptionDescriptions(Console.Out);
return;
}
sourcesCmdSet.Run(pargs);
}
};
var add = new Command("add")
{
Run = sargs => Add(sargs)
};
commandSet.Add(add);
var pushCmd = new Command(push)
{
Run = sargs =>
{
var pargs = pushoptions.Parse(sargs);
if (args.Count()==0) shouldShowPushHelp=true;
if (shouldShowPushHelp)
{
// output the options
Console.Error.WriteLine("Push Options:");
pushoptions.WriteOptionDescriptions(Console.Out);
return;
}
List<PushReport> reports = PushPkg(pargs);
}
};
var setapikey = new Command("store-api-key", "Store this API key")
{
Run = sargs => StoreApiKey(sargs)
};
setapikey.Options = pushoptions;
commandSet.Add(setapikey);
commandSet.Add(pushCmd);
commandSet.Add(srcCmd);
commandSet.Add(showCommand);
List<string> extra;
try
{
// parse the command line
extra = options.Parse(args);
}
catch (OptionException e)
{
Console.Error.Write("isn: ");
Console.Error.WriteLine(e.Message);
Console.Error.WriteLine("Try `isn --help' for more information.");
return 2;
}
if (shouldShowHelp)
{
// output the options
Console.WriteLine("Options:");
options.WriteOptionDescriptions(Console.Out);
return 1;
}
if (shouldShowVersion)
{
Console.WriteLine("isn version " + GitVersionInformation.AssemblySemFileVer);
}
return commandSet.Run(args);
}
}
}

90
src/isn/Settings.cs Normal file
View File

@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Security.Cryptography;
using System.Text;
using Newtonsoft.Json;
namespace isn
{
public class SourceSettings
{
/// <summary>
/// Protected API Key
/// </summary>
/// <value></value>
public string ApiKey { get; set; }
/// <summary>
/// Key alias
/// </summary>
/// <value></value>
public string Url { get; set; }
public SourceSettings()
{
}
public string GetClearApiKey()
{
return ApiKey;
}
public void SetApiKey(string key)
{
ApiKey = key;
}
}
public class Settings
{
private Settings()
{
}
public static Settings Create()
{
return new Settings {
Sources = new Dictionary<string, SourceSettings>()
};
}
public Dictionary<string, SourceSettings> Sources { get; set; }
private string defSourceKey;
/// <summary>
/// Default source by its alias
/// </summary>
/// <value></value>
public string DefaultSourceKey
{
get => defSourceKey;
set
{
defSourceKey = value;
}
}
[JsonIgnore, NotMapped]
public string CurrentSourceKey {get; set;}
[JsonIgnore, NotMapped]
public SourceSettings CurrentSource
{
get
{
if (CurrentSourceKey==null) return null;
if (!Sources.ContainsKey(CurrentSourceKey))
{
Sources.Add(CurrentSourceKey, new SourceSettings());
}
return Sources[CurrentSourceKey];
}
}
}
}

40
src/isn/SourceHelpers.cs Normal file
View File

@ -0,0 +1,40 @@
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using isn.Abstract;
namespace isn
{
public static class SourceHelpers
{
public static ApiIndexViewModel GetServerResources(string url)
{
HttpClient client = new HttpClient();
ApiIndexViewModel result = null;
// var json = await client.GetStringAsync(new System.Uri(url));
Task.Run(async ()=> {
try {
var response = await client.GetStringAsync(url);
result = JsonConvert.DeserializeObject<ApiIndexViewModel>(response);
} catch (HttpRequestException ex)
{
if (ex.StatusCode==HttpStatusCode.NotFound)
{
Console.Error.WriteLine("Not found ... server's down ?");
}
else
{
Console.Error.WriteLine($"{ex.StatusCode} : {ex.Message}");
}
}
}).Wait();
return result;
}
}
}

View File

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace isn
{
public static class UploadFilesToServerUsingHttpClient
{
public static PushReport UploadFilesToServer(this HttpClient client, Uri uri, FileInfo fi,
string apikey)
{
return UploadFilesToServerAsync(client, uri, fi, apikey).Result;
}
public static async Task<PushReport> UploadFilesToServerAsync(this HttpClient client, Uri uri, FileInfo fi,
string apikey)
{
client.DefaultRequestHeaders.Add("X-NuGet-Client-Version", Constants.ClientVersion);
client.DefaultRequestHeaders.Add("X-NuGet-ApiKey", apikey);
using (var multipartFormDataContent = new MultipartFormDataContent())
{
multipartFormDataContent.Add(new ByteArrayContent(File.ReadAllBytes(fi.FullName)),
'"' + "File" + '"',
'"' + fi.Name + '"');
var result = await client.PutAsync(uri, multipartFormDataContent);
if (result.IsSuccessStatusCode) return
new PushReport() {
KO = JsonConvert.DeserializeObject<APIKO>(await result.Content.ReadAsStringAsync())
};
else
return new PushReport() {
OK = true
};
}
}
}
}

View File

@ -0,0 +1,75 @@
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace isn
{
public class PushCommand
{
Settings settings;
public PushCommand(Settings settings)
{
this.settings = settings;
}
public PushReport Run(string pkg, string source, string apiKey)
{
var resources = SourceHelpers.GetServerResources(source);
if (resources == null) return null;
if (resources.Resources == null)
throw new InvalidOperationException("source gave no resource");
if (!resources.Resources.Any(res => res.Type == "PackagePublish/2.0.0"))
throw new InvalidOperationException("Source won't serve the expected push command");
var pubRes = resources.Resources.First(res => res.Type == "PackagePublish/2.0.0");
FileInfo fi = new FileInfo(pkg);
if (!fi.Exists)
{
var report = new PushReport
{
PkgName = fi.Name,
Message = "The package does not exist : " + fi.FullName
};
return report;
}
using (var client = new HttpClient(
new HttpClientHandler
{
AllowAutoRedirect = false
}
))
try
{
return client.UploadFilesToServer(new Uri(pubRes.Id), fi, settings.Sources[source].ApiKey);
}
catch (HttpRequestException httpEx)
{
var report = new PushReport
{
PkgName = fi.Name,
Message = "HttpRequest: " + httpEx.Message,
StackTrace = httpEx.StackTrace,
StatusCode = httpEx.HResult.ToString()
};
Console.Error.WriteLine(httpEx.Message);
return report;
}
catch (Exception ex)
{
var report = new PushReport
{
PkgName = fi.Name,
Message = ex.Message,
StackTrace = ex.StackTrace
};
Console.Error.WriteLine(ex.Message);
return report;
}
}
}
}

33
src/isn/commands/push.cs Normal file
View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace isn
{
partial class Program
{
public static List<PushReport> PushPkg(IEnumerable<string> pkgs)
{
List<PushReport> pushReports = new List<PushReport>();
var cmd = new PushCommand(Settings);
if (Settings.CurrentSource == null) throw new InvalidOperationException("source is null");
var source = Settings.CurrentSource;
foreach (string pkg in pkgs)
{
var report = cmd.Run(pkg, source.Url, source.ApiKey);
pushReports.Add(report);
}
return pushReports;
}
private static object Add(IEnumerable<string> str)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace isn
{
partial class Program
{
private static void ShowConfig()
{
Console.WriteLine(JsonConvert.SerializeObject(Settings, Formatting.Indented));
}
}
}

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace isn
{
partial class Program
{
private static void SourceAdd(IEnumerable<string> str)
{
foreach (string arg in str)
{
if (Settings.Sources.ContainsKey(arg))
{
SourceSettings setting = Settings.Sources[arg];
throw new InvalidOperationException
(setting.Url);
}
throw new NotImplementedException();
}
}
}
}

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
namespace isn
{
partial class Program
{
private static void SourceList(IEnumerable<string> sargs)
{
IEnumerable<string> spec = sargs.Count()>0 ? sargs : Settings.Sources.Keys;
foreach (string arg in spec)
{
SourceSettings setting = Settings.Sources[arg];
Console.WriteLine(JsonConvert.SerializeObject(setting));
}
}
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Linq;
namespace isn
{
partial class Program
{
private static void SetDefaultSource(string arg)
{
SourceSettings settings =
Settings.Sources.ContainsKey(arg) ?
Settings.Sources[arg] :
Settings.Sources.Values.FirstOrDefault((s)=> s.Url == arg) ;
if (settings==null) throw new InvalidOperationException(arg);
Settings.DefaultSourceKey = arg;
SaveConfig();
}
}
}

View File

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
namespace isn
{
partial class Program
{
private static void StoreApiKey(IEnumerable<string> storeArgs)
{
var args = storeoptions.Parse(storeArgs);
if (args.Count != 1)
{
Console.Error.WriteLine("StoreApiKey command takes only one argument, the key.");
shouldShowPushHelp=true;
}
if (shouldShowPushHelp)
{
// output the options
Console.Error.WriteLine("StoreApiKey Options:");
storeoptions.WriteOptionDescriptions(Console.Out);
}
else
{
Settings.CurrentSource.SetApiKey(args[0]);
SaveConfig();
}
}
public static void SaveConfig()
{
FileInfo cfgSettingIf = new FileInfo(_configFileName);
if (!cfgSettingIf.Directory.Exists) cfgSettingIf.Directory.Create();
File.WriteAllText(
cfgSettingIf.FullName,
JsonConvert.SerializeObject(
Settings,
Formatting.Indented
));
Console.WriteLine("config saved .");
}
}
}

22
src/isn/isn.csproj Normal file
View File

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net8.0</TargetFrameworks>
<RootNamespace>nuget_cli</RootNamespace>
<UserSecretsId>45b74c62-05bc-4603-95b4-3e80ae2fdf50</UserSecretsId>
<IsPackable>true</IsPackable>
<PackageLicenseExpression>WTFPL</PackageLicenseExpression>
<IsTool>true</IsTool>
<NoWarn>NETSDK1138</NoWarn>
<InformationalVersion>1.0.7+Branch.main.Sha.3695c1742965d93eba0ad851656cfaa3e44ba327</InformationalVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Security.Cryptography.Pkcs" Version="8.0.1" />
<PackageReference Include="Mono.Options" Version="5.3.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="unleash.client" Version="4.1.13" />
<PackageReference Include="GitVersion.MsBuild" Version="6.0.3" />
<ProjectReference Include="../isn.abst/isn.abst.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,42 @@
using System.Threading.Tasks.Dataflow;
using System.Net;
using System.Text;
using Newtonsoft.Json;
namespace isn
{
public class PushReport
{
public PushReport()
{
}
public string PkgName { get; set; }
public bool Executed { get; set; }
public bool OK { get; set; }
public bool AlreadyPresent { get; set; }
public string Message { get; set; }
public string StatusCode { get; set; }
public string StackTrace { get; set; }
public APIKO KO { get; set; }
public string ToDoc()
{
StringBuilder sb = new StringBuilder($"= push {PkgName}\n\n");
if (Executed) sb.AppendLine("* Executed");
if (OK) sb.AppendLine("* OK");
if (!string.IsNullOrWhiteSpace(Message))
sb.AppendLine("* Message :" + Message);
if (!string.IsNullOrWhiteSpace(StatusCode))
sb.AppendLine($"* Status Code : ");
if (!string.IsNullOrWhiteSpace(StackTrace))
sb.AppendLine($"* StackTrace : " + StackTrace);
if (KO!=null)
sb.AppendLine($"* KO : " + KO.Context);
return sb.ToString();
}
}
}

View File

@ -0,0 +1,8 @@
using Microsoft.AspNetCore.Authorization;
namespace isnd.Authorization
{
internal class ValidApiKeyRequirement : IAuthorizationRequirement
{
}
}

View File

@ -0,0 +1,13 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
namespace isnd.Authorization
{
internal class ValidApiKeyRequirementHandler : AuthorizationHandler<ValidApiKeyRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ValidApiKeyRequirement requirement)
{
throw new System.NotImplementedException();
}
}
}

9
src/isnd/Constants.cs Normal file
View File

@ -0,0 +1,9 @@
namespace isnd
{
public static class IsndConstants
{
public const string AdministratorRoleName = "Admin";
public const string RequireAdminPolicyName = "RequireAdministratorRole";
public const string RequireValidApiKey = "RequireValideApiKey";
}
}

View File

@ -0,0 +1,258 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using isnd.Data;
using isnd.Data.Roles;
using System;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
namespace isnd.Controllers
{
[AllowAnonymous]
public class AccountController : Controller
{
private readonly IAuthenticationSchemeProvider _schemeProvider;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly UserManager<ApplicationUser> _userManager;
private readonly AdminStartupList _startupAdminList;
public AccountController(
IAuthenticationSchemeProvider schemeProvider,
SignInManager<ApplicationUser> signInManager,
UserManager<ApplicationUser> userManager,
IOptions<AdminStartupList> startupAdminListConfig )
{
_schemeProvider = schemeProvider;
_signInManager = signInManager;
_userManager = userManager;
_startupAdminList = startupAdminListConfig.Value;
}
/// <summary>
/// Entry point into the login workflow
/// </summary>
[HttpGet]
public async Task<IActionResult> Login(string returnUrl)
{
// build a model so we know what to show on the login page
var vm = await BuildLoginViewModelAsync(returnUrl);
if (vm.IsExternalLoginOnly)
{
// we only have one option for logging in and it's an external provider
return RedirectToAction("Challenge", "External", new { scheme = vm.ExternalLoginScheme, returnUrl });
}
return View(vm);
}
/// <summary>
/// Handle postback from username/password login
/// </summary>
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginInputModel model, string button)
{
// the user clicked the "cancel" button
if (button != "login")
{
// since we don't have a valid context, then we just go back to the home page
return Redirect("~/");
}
if (ModelState.IsValid)
{
// validate username/password
var user = await _userManager.FindByNameAsync(model.Username);
var signResult = await _signInManager.CheckPasswordSignInAsync(user, model.Password, true);
if (signResult.Succeeded)
{
// only set explicit expiration here if user chooses "remember me".
// otherwise we rely upon expiration configured in cookie middleware.
AuthenticationProperties props = null;
if (AccountOptions.AllowRememberLogin && model.RememberLogin)
{
props = new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration)
};
};
await _signInManager.SignInAsync(user, model.RememberLogin && AccountOptions.AllowRememberLogin);
if (Url.IsLocalUrl(model.ReturnUrl))
{
return Redirect(model.ReturnUrl);
}
else if (string.IsNullOrEmpty(model.ReturnUrl))
{
return Redirect("~/");
}
else
{
// user might have clicked on a malicious link - should be logged
throw new Exception("invalid return URL");
}
}
ModelState.AddModelError(string.Empty, AccountOptions.InvalidCredentialsErrorMessage);
}
// something went wrong, show form with error
var vm = await BuildLoginViewModelAsync(model);
return View(vm);
}
/// <summary>
/// Show logout page
/// </summary>
[HttpGet]
public async Task<IActionResult> Logout(string logoutId)
{
// build a model so the logout page knows what to display
var vm = BuildLogoutViewModel(logoutId);
if (vm.ShowLogoutPrompt == false)
{
// if the request for logout was properly authenticated from IdentityServer, then
// we don't need to show the prompt and can just log the user out directly.
return await Logout(vm);
}
return View(vm);
}
/// <summary>
/// Handle logout page postback
/// </summary>
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout(LogoutInputModel model)
{
// build a model so the logged out page knows what to display
var vm = BuildLoggedOutViewModel(model.LogoutId);
if (User?.Identity.IsAuthenticated == true)
{
// delete local authentication cookie
await HttpContext.SignOutAsync();
}
// check if we need to trigger sign-out at an upstream identity provider
if (vm.TriggerExternalSignout)
{
// build a return URL so the upstream provider will redirect back
// to us after the user has logged out. this allows us to then
// complete our single sign-out processing.
string url = Url.Action("Logout", new { logoutId = vm.LogoutId });
// this triggers a redirect to the external provider for sign-out
return SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme);
}
return View("LoggedOut", vm);
}
[HttpGet]
public IActionResult AccessDenied()
{
return new BadRequestObjectResult(403);
}
/*****************************************/
/* helper APIs for the AccountController */
/*****************************************/
private async Task<LoginViewModel> BuildLoginViewModelAsync(string returnUrl)
{
var schemes = await _schemeProvider.GetAllSchemesAsync();
var providers = schemes
.Where(x => x.DisplayName != null)
.Select(x => new ExternalProvider
{
DisplayName = x.DisplayName ?? x.Name,
AuthenticationScheme = x.Name
}).ToList();
var allowLocal = true;
return new LoginViewModel
{
AllowRememberLogin = AccountOptions.AllowRememberLogin,
EnableLocalLogin = allowLocal && AccountOptions.AllowLocalLogin,
ReturnUrl = returnUrl,
ExternalProviders = providers.ToArray()
};
}
private async Task<LoginViewModel> BuildLoginViewModelAsync(LoginInputModel model)
{
var vm = await BuildLoginViewModelAsync(model.ReturnUrl);
vm.Username = model.Username;
vm.RememberLogin = model.RememberLogin;
return vm;
}
private LogoutViewModel BuildLogoutViewModel(string logoutId)
{
var vm = new LogoutViewModel { LogoutId = logoutId, ShowLogoutPrompt = AccountOptions.ShowLogoutPrompt };
if (User?.Identity.IsAuthenticated != true)
{
// if the user is not authenticated, then just show logged out page
vm.ShowLogoutPrompt = false;
return vm;
}
// show the logout prompt. this prevents attacks where the user
// is automatically signed out by another malicious web page.
return vm;
}
private LoggedOutViewModel BuildLoggedOutViewModel(string logoutId)
{
var vm = new LoggedOutViewModel
{
AutomaticRedirectAfterSignOut = AccountOptions.AutomaticRedirectAfterSignOut,
LogoutId = logoutId
};
return vm;
}
[Authorize]
public async Task<IActionResult> GetAdminrole()
{
string username = User.FindFirstValue(ClaimTypes.NameIdentifier);
if (_startupAdminList.Users.Contains(username))
{
var user = await _userManager.FindByNameAsync(username);
var roles = await _userManager.GetRolesAsync(user);
if (!roles.Contains(IsndConstants.AdministratorRoleName))
{
await _userManager.AddToRoleAsync(user, IsndConstants.AdministratorRoleName);
}
return Ok();
}
return BadRequest();
}
}
}

View File

@ -0,0 +1,148 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using isnd.Data;
using isnd.Entities;
using isnd.Data.ApiKeys;
using isnd.Interfaces;
namespace isnd.Controllers
{
[Authorize]
public class ApiKeysController : Controller
{
private readonly ApplicationDbContext dbContext;
private readonly IsndSettings isndSettings;
private readonly UserManager<ApplicationUser> _userManager;
private readonly IApiKeyProvider apiKeyProvider;
private readonly IDataProtector protector;
public ApiKeysController(ApplicationDbContext dbContext,
IOptions<IsndSettings> isndSettingsOptions,
IDataProtectionProvider provider,
UserManager<ApplicationUser> userManager,
IApiKeyProvider apiKeyProvider
)
{
this.dbContext = dbContext;
this.isndSettings = isndSettingsOptions.Value;
protector = provider.CreateProtector(isndSettings.ProtectionTitle);
_userManager = userManager;
this.apiKeyProvider = apiKeyProvider;
}
[HttpGet]
public async Task<ActionResult> Index()
{
List<ApiKey> index = await GetUserKeys().ToListAsync();
IndexModel model = new IndexModel { ApiKey = index };
ViewData["Title"] = "Index";
return View("Index", model);
}
[HttpGet]
public async Task<ActionResult> Create()
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
var user = await _userManager.FindByIdAsync(userId);
ViewBag.UserId = new SelectList(new List<ApplicationUser> { user });
return View(new CreateModel{ });
}
[HttpPost]
public async Task<ActionResult> Create(CreateModel model)
{
string userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
IQueryable<ApiKey> userKeys = apiKeyProvider.GetUserKeys(User.Identity.Name);
if (userKeys.Count() >= isndSettings.MaxUserKeyCount)
{
ModelState.AddModelError(null, "Maximum key count reached");
return View();
}
model.UserId = userId;
ApiKey newKey = await apiKeyProvider.CreateApiKeyAsync(model);
return View("Details", new DetailModel { Name = newKey.Name,
ProtectedValue = protector.Protect(newKey.Id),
ApiKey = newKey });
}
[HttpGet]
public async Task<ActionResult> Delete(string id)
{
string userid = User.FindFirstValue(ClaimTypes.NameIdentifier);
ApiKey key = await dbContext.ApiKeys.FirstOrDefaultAsync(k => k.Id == id && k.UserId == userid);
return View(new DeleteModel { ApiKey = key });
}
[HttpPost]
public async Task<ActionResult> Delete(DeleteModel model)
{
string userid = User.FindFirstValue(ClaimTypes.NameIdentifier);
ApiKey key = dbContext.ApiKeys.FirstOrDefault(k => k.Id == model.ApiKey.Id && k.UserId == userid);
if (key == null)
{
ModelState.AddModelError(null, "Key not found");
return View();
}
_ = dbContext.ApiKeys.Remove(key);
_ = await dbContext.SaveChangesAsync();
return View("Index", new IndexModel { ApiKey = GetUserKeys().ToList() } );
}
public async Task<ActionResult> Details(string id)
{
string userid = User.FindFirstValue(ClaimTypes.NameIdentifier);
ApiKey key = await dbContext.ApiKeys.FirstOrDefaultAsync(k => k.Id == id && k.UserId == userid);
if (key == null)
{
ModelState.AddModelError("id", "Key not found");
return View();
}
return View("Details", new DetailModel { ApiKey = key, Name = key.Name, ProtectedValue = protector.Protect(key.Id)});
}
public async Task<ActionResult> Edit(string id)
{
EditModel edit = new EditModel();
string userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
var user = await _userManager.FindByIdAsync(userId);
edit.ApiKey = await GetUserKeys().SingleOrDefaultAsync(k =>
k.UserId == userId && k.Id == id);
ViewBag.UserId = new SelectList(new List<ApplicationUser> { user });
return View(edit);
}
[HttpPost]
public async Task<ActionResult> Edit(EditModel model)
{
string userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
var apiKey = await dbContext.ApiKeys.SingleOrDefaultAsync(k => k.UserId == userId && k.Id == model.ApiKey.Id);
apiKey.Name = model.ApiKey.Name;
apiKey.ValidityPeriodInDays = model.ApiKey.ValidityPeriodInDays;
await dbContext.SaveChangesAsync();
return View("Details", new DetailModel { ApiKey = apiKey });
}
public IQueryable<ApiKey> GetUserKeys()
{
return dbContext.ApiKeys.Include(k => k.User).Where(k => k.User.UserName == User.Identity.Name);
}
}
}

View File

@ -0,0 +1,79 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using isnd.Data;
using System.Linq;
using isnd.ViewModels;
using System.Reflection;
using Microsoft.EntityFrameworkCore;
using Microsoft.Build.Logging;
using Microsoft.Extensions.Options;
using isnd.Entities;
using isn.abst;
namespace isnd.Controllers
{
/// <summary>
/// Home Controller
/// </summary>
public class HomeController : Controller
{
private readonly ApplicationDbContext _dbContext;
private readonly IsndSettings _isndSettings;
/// <summary>
/// Home controller ctor
/// </summary>
/// <param name="dbContext"></param>
/// <param name="isndSettings"></param>
public HomeController(
ApplicationDbContext dbContext, IOptions<IsndSettings> isndSettings)
{
_dbContext = dbContext;
_isndSettings = isndSettings.Value;
}
public IActionResult Index()
{
return View(new HomeIndexViewModel{
PkgCount = _dbContext.Packages
.Where(p => p.Versions.Count > 0)
.Count(),
APIUrl = _isndSettings.ExternalUrl + Constants.ApiVersionPrefix + ApiConfig.Index});
}
public IActionResult About()
{
ViewData["Message"] = "Your application description page.";
return View();
}
public IActionResult Contact()
{
ViewData["Message"] = "Your contact page.";
var ass = typeof(isn.Abstract.Resource).GetType().Assembly;
var attrs = ass.GetCustomAttributes(true);
// ass.GetCustomAttributes(true);
System.Reflection.AssemblyFileVersionAttribute v = (AssemblyFileVersionAttribute)
GetType().Assembly.GetCustomAttribute(typeof(AssemblyFileVersionAttribute));
return View();
}
public IActionResult Privacy()
{
ViewData["Message"] = "Your Privacy page.";
return View(ViewData);
}
public IActionResult Error(string id)
{
return View("Error", id);
}
}
}

View File

@ -0,0 +1,18 @@
using System;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using isnd.Data;
namespace isnd.Controllers
{
// TODO Web hook CI
public class NewUpdateController : Controller
{
[Authorize(Policy = IsndConstants.RequireAdminPolicyName)]
public IActionResult NewRelease(NewReleaseInfo release)
{
throw new NotImplementedException("web hook");
}
}
}

View File

@ -0,0 +1,54 @@
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using isnd.Data;
using isnd.ViewModels;
using isnd.Helpers;
using isnd.Interfaces;
namespace isnd
{
[AllowAnonymous]
public class PackageVersionController : Controller
{
private readonly ApplicationDbContext _context;
private readonly IPackageManager _pm;
public PackageVersionController(ApplicationDbContext context,
IPackageManager pm)
{
_context = context;
_pm = pm;
}
// GET: PackageVersion
public async Task<IActionResult> Index(PackageVersionIndexViewModel model)
{
var applicationDbContext = _context.PackageVersions.Include(p => p.Package)
.Include(p => p.Package.Owner)
.Include(p => p.Package.Versions)
.Where(
p => (model.Prerelease || !p.IsPrerelease)
&& ((model.PackageId == null) || p.PackageId.StartsWith(model.PackageId)));
model.Versions = await applicationDbContext.ToArrayAsync();
return View(model);
}
[Authorize]
public async Task<IActionResult> Mines(PackageVersionIndexViewModel model)
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
var applicationDbContext = _context.PackageVersions
.Include(p => p.Package).Where(
p => (string.IsNullOrEmpty(model.PackageId) || p.PackageId.StartsWith(model.PackageId))
&& p.Package.OwnerId == userId);
model.Versions = await applicationDbContext.ToArrayAsync();
return View("Index", model);
}
}
}

View File

@ -0,0 +1,40 @@
using Microsoft.AspNetCore.Mvc;
using isnd.Services;
using isn.Abstract;
using isn.abst;
using isnd.Interfaces;
using System.Linq;
using isnd.Entities;
namespace isnd.Controllers
{
/// <summary>
/// Api Controller
/// </summary>
public class ApiController : Controller
{
private readonly IPackageManager packageManager;
private readonly Resource[] resources;
/// <summary>
/// Api Controller Constructor
/// </summary>
/// <param name="pm"></param>
public ApiController(IPackageManager pm)
{
packageManager = pm;
resources = packageManager.GetResources().ToArray();
}
/// <summary>
/// API index
/// </summary>
/// <returns></returns>
[HttpGet("~" + Constants.ApiVersionPrefix + ApiConfig.Index)]
public IActionResult ApiIndex()
{
return Ok(new ApiIndexViewModel(packageManager.CatalogBaseUrl + ApiConfig.Index){ Version = PackageManager.BASE_API_LEVEL, Resources = resources });
}
}
}

View File

@ -0,0 +1,18 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using isnd.Entities;
using isn.abst;
namespace isnd.Controllers
{
public partial class PackagesController
{
[HttpGet("~" + Constants.ApiVersionPrefix + ApiConfig.PackageDetailUriTemplate)]
public async Task<IActionResult> Details(string id, string version)
{
var result = await packageManager.GetPackageDetailsAsync(id, version);
if (result==null) return NotFound();
return Ok(result);
}
}
}

View File

@ -0,0 +1,34 @@
using isnd.Services;
using isnd.Entities;
using Microsoft.AspNetCore.Mvc;
using isn.abst;
namespace isnd.Controllers
{
public partial class PackagesController
{
// GET /autocomplete?id=isn.protocol&prerelease=true
[HttpGet("~" + Constants.ApiVersionPrefix + ApiConfig.AutoComplete)]
public IActionResult AutoComplete(
string id,
string semVerLevel,
bool prerelease = false,
string packageType = null,
int skip = 0,
int take = 25)
{
if (take > maxTake)
{
ModelState.AddModelError("take", "Maximum exceeded");
}
if (semVerLevel != PackageManager.BASE_API_LEVEL)
{
ModelState.AddModelError("semVerLevel", PackageManager.BASE_API_LEVEL + " expected");
}
if (ModelState.ErrorCount > 0) return BadRequest(ModelState);
return Ok(packageManager.AutoComplete(id,skip,take,prerelease,packageType));
}
}
}

View File

@ -0,0 +1,43 @@
using System.Threading.Tasks;
using isnd.Entities;
using Microsoft.AspNetCore.Mvc;
using isn.abst;
namespace isnd.Controllers
{
public partial class PackagesController
{
// https://docs.microsoft.com/en-us/nuget/api/catalog-resource#versioning
[HttpGet("~" + Constants.ApiVersionPrefix + ApiConfig.Catalog)]
public async Task<IActionResult> CatalogIndex()
{
return Ok(await packageManager.GetCatalogIndexAsync());
}
[HttpGet("~" + Constants.ApiVersionPrefix + ApiConfig.Registration + "/{id}/{version}.json")]
public async Task<IActionResult> CatalogRegistration(string id, string version)
{
if ("index" == version)
{
var query = new Data.Catalog.PackageRegistrationQuery
{
Query = id,
Prerelease = true
};
var index = await packageManager.GetPackageRegistrationIndexAsync(query);
if (index == null) return NotFound();
return Ok(index);
}
// return Package details
var leaf = await packageManager.GetPackageDetailsAsync(id, version, null);
if (null == leaf) return NotFound(new { id, version });
return Ok(leaf);
}
}
}

View File

@ -0,0 +1,25 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using isnd.Helpers;
using isnd.Entities;
using System.ComponentModel.DataAnnotations;
using isnd.Attributes;
using System.Security.Claims;
using isn.abst;
namespace isnd.Controllers
{
public partial class PackagesController
{
[HttpDelete("~" + Constants.ApiVersionPrefix + ApiConfig.Package + "/{id}/{lower?}/{type?}")]
public async Task<IActionResult> ApiDelete(
[FromRoute][SafeName][Required] string id,
[FromRoute][SafeName][Required] string lower,
[FromRoute] string type)
{
var uid = User.FindFirstValue(ClaimTypes.NameIdentifier);
var report = await packageManager.UserAskForPackageDeletionAsync(uid, id, lower, type);
return Ok(report);
}
}
}

View File

@ -0,0 +1,55 @@
using System.ComponentModel.DataAnnotations;
using System.IO;
using Microsoft.AspNetCore.Mvc;
using isnd.Attributes;
using isnd.Entities;
using isn.abst;
namespace isnd.Controllers
{
public partial class PackagesController
{
[HttpGet("~" + Constants.ApiVersionPrefix + ApiConfig.ContentBase + "/{id}/{version}/{idf}.{versionFromName}."
+ Constants.PacketFileExtension)]
public IActionResult GetPackage(
[FromRoute][SafeName][Required] string id,
[FromRoute][SafeName][Required] string version,
[FromRoute] string idf, [FromRoute] string versionFromName)
{
var pkgPath = Path.Combine(isndSettings.PackagesRootDir,
id, version, $"{id}-{version}." + Constants.PacketFileExtension
);
FileInfo pkgFileInfo = new FileInfo(pkgPath);
if (!pkgFileInfo.Exists)
{
return BadRequest("Package does´nt exist in the file system.");
}
return File(pkgFileInfo.OpenRead(), "application/zip; charset=binary");
}
// Web get spec
[HttpGet("~" + Constants.ApiVersionPrefix + ApiConfig.ContentBase + "/{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 lowerFromName)
{
var pkgPath = Path.Combine(isndSettings.PackagesRootDir,
id, lower, $"{id}." + Constants.SpecFileExtension);
FileInfo pkgFileInfo = new FileInfo(pkgPath);
if (!pkgFileInfo.Exists)
{
return BadRequest("Package info does´nt exist in the file system.");
}
return File(pkgFileInfo.OpenRead(), "text/xml; charset=utf-8");
}
}
}

View File

@ -0,0 +1,47 @@
using Microsoft.AspNetCore.Mvc;
using NuGet.Versioning;
using isnd.Entities;
using isn.abst;
namespace isnd.Controllers
{
public partial class PackagesController
{
[HttpGet("~" + Constants.ApiVersionPrefix + ApiConfig.ContentBase + "/{id}/{version}.json")]
public IActionResult GetVersions(
string id,
string version,
bool prerelease = false,
string packageType = null,
int skip = 0,
int take = 50)
{
NuGetVersion parsedVersion=null;
if (take > maxTake)
{
ModelState.AddModelError("take", "Maximum exceeded");
}
if ("index"==version)
{
version=null;
}
if (version!=null)
{
if (!NuGetVersion.TryParse(version, out parsedVersion))
{
ModelState.AddModelError("version", "invalid version string");
}
}
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
return Ok(new
{
versions = packageManager.GetVersions(
id, parsedVersion, prerelease, packageType, skip, take)
});
}
}
}

View File

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using isnd.Entities;
using Microsoft.AspNetCore.Http;
using isn.abst;
namespace isnd.Controllers
{
public partial class PackagesController
{
// TODO [Authorize(Policy = IsndConstants.RequireValidApiKey)]
[HttpPut("~" + Constants.ApiVersionPrefix + ApiConfig.Package)]
public async Task<IActionResult> Put()
{
try
{
var clientVersionId = Request.Headers["X-NuGet-Client-Version"];
string apiKey = Request.Headers["X-NuGet-ApiKey"][0];
ViewData["versionId"] = typeof(PackagesController).Assembly.FullName;
var files = new List<string>();
ViewData["files"] = files;
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();
}
foreach (IFormFile file in Request.Form.Files)
{
var version = await packageManager.PutPackageAsync(file.OpenReadStream(), dbApiKey.UserId);
logger.LogInformation($"new package : {version.PackageId} {version.NugetLink}");
}
return Ok();
}
catch (Exception ex)
{
var message = $"PUT exception : {ex.Message} ({ex.GetType().Name})";
logger.LogError(message);
logger.LogError("Stack Trace : " + ex.StackTrace);
return new ObjectResult(new { ViewData, message })
{ StatusCode = 500 };
}
}
}
}

View File

@ -0,0 +1,37 @@
using Microsoft.AspNetCore.Mvc;
using isnd.Entities;
using isn.abst;
using System.Threading.Tasks;
using isnd.Data.Catalog;
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.Search)]
[HttpHead("~" + Constants.ApiVersionPrefix + ApiConfig.Search)]
[HttpHead("~" + ApiConfig.V2Find)]
public async Task<IActionResult> Search(
string q=null,
int skip=0,
int take=25,
bool prerelease=false,
string semVerLevel = "2.0.0",
string packageType = "Dependency")
{
PackageRegistrationQuery query = new PackageRegistrationQuery
{
Prerelease= prerelease,
Query = q,
Skip = skip,
Take = take
};
var result = await packageManager.SearchPackageAsync(query);
return Ok(result);
}
}
}

View File

@ -0,0 +1,114 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using isn.abst;
using isnd.Data;
using isnd.Data.Catalog;
using isnd.Entities;
using isnd.Helpers;
using isnd.ViewModels;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components.Web.Virtualization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace isnd.Controllers
{
public partial class PackagesController
{
// Web search
public async Task<IActionResult> Index(PackageRegistrationQuery model)
{
var pkgs = await packageManager.SearchPackageAsync(model);
var result = new RegistrationPageIndexQueryAndResult
{
Source = packageManager.CatalogBaseUrl+ApiConfig.Index,
Query = model
};
List<Catalog> registrations = new List<Catalog>();
if (pkgs.data!=null)
if (pkgs.data.Length>0)
{
var grouped = pkgs.data.GroupBy(x => x.PackageId);
if (grouped!=null) if (grouped.Count()>0)
{
foreach (var pk in grouped.ToArray())
{
registrations.Add(new Catalog(apiBase, pk.Key, pkgs.GetResults().Single(p=>p.Id == pk.Key).Versions));
}
}
}
return View(new RegistrationPageIndexQueryAndResult
{
Source = packageManager.CatalogBaseUrl+ApiConfig.Index,
Query = model,
Result = registrations.ToArray()
});
}
public async Task<IActionResult> Details(PackageDetailViewModel model)
{
if (model.pkgid == null)
{
return NotFound();
}
var packageVersion = dbContext.PackageVersions
.Include(p=>p.LatestCommit)
.Include(p => p.Package)
.Where(m => m.PackageId == model.pkgid)
.OrderByDescending(p => p)
;
if (packageVersion.Count() == 0)
{
return NotFound();
}
bool results = await packageVersion.AnyAsync();
model.latest = await packageVersion.FirstAsync();
model.totalHits = packageVersion.Count();
model.data = packageVersion.Take(MAX_PKG_VERSION_LIST).ToArray();
return View("Details", model);
}
const int MAX_PKG_VERSION_LIST = 50;
[Authorize, HttpGet]
public async Task<IActionResult> Delete(string pkgid, string version, string pkgtype)
{
if (pkgid == null || version == null)
{
return NotFound();
}
var packageVersion = await dbContext.PackageVersions.Include(p => p.Package)
.FirstOrDefaultAsync(m => m.PackageId == pkgid && m.FullString == version
&& (pkgtype!=null && m.Type == pkgtype || m.Type != "Delete" ));
if (packageVersion == null) return NotFound();
if (!User.IsOwner(packageVersion)) return Challenge();
var pkg = await packageManager.GetPackageDetailsAsync(pkgid, version, pkgtype);
return View(pkg);
}
// POST: PackageVersion/Delete/5
[HttpPost]
[ValidateAntiForgeryToken][Authorize]
public async Task<IActionResult> DeleteConfirmed(string pkgid, string version,
string type)
{
var packageVersion = await dbContext.PackageVersions
.Include(pv => pv.Package)
.Where(m => m.PackageId == pkgid
&& m.FullString == version && (type==null || m.Type == type))
.ToArrayAsync();
if (packageVersion.Length==0) return NotFound();
if (!User.IsOwner(packageVersion.First())) return Unauthorized();
await packageManager.DeletePackageAsync(pkgid, version, type);
return RedirectToAction(nameof(Index));
}
}
}

View File

@ -0,0 +1,46 @@
using System.Linq;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using isnd.Data;
using isnd.Entities;
using isnd.Interfaces;
using isn.Abstract;
using isn.abst;
namespace isnd.Controllers
{
[AllowAnonymous]
public partial class PackagesController : Controller
{
const int maxTake = 100;
private readonly Resource[] resources;
private readonly string apiBase;
private readonly ILogger<PackagesController> logger;
private readonly IDataProtector protector;
private readonly IsndSettings isndSettings;
readonly ApplicationDbContext dbContext;
private readonly IPackageManager packageManager;
public PackagesController(
ILoggerFactory loggerFactory,
IDataProtectionProvider provider,
IOptions<IsndSettings> isndOptions,
ApplicationDbContext dbContext,
IPackageManager pm)
{
logger = loggerFactory.CreateLogger<PackagesController>();
isndSettings = isndOptions.Value;
protector = provider.CreateProtector(isndSettings.ProtectionTitle);
this.dbContext = dbContext;
packageManager = pm;
resources = packageManager.GetResources().ToArray();
this.apiBase = isndSettings.ExternalUrl + Constants.ApiVersionPrefix;
}
}
}

View File

@ -0,0 +1,20 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
using System;
namespace isnd.Data
{
public class AccountOptions
{
public static bool AllowLocalLogin = true;
public static bool AllowRememberLogin = true;
public static TimeSpan RememberMeLoginDuration = TimeSpan.FromDays(30);
public static bool ShowLogoutPrompt = true;
public static bool AutomaticRedirectAfterSignOut = false;
public static string InvalidCredentialsErrorMessage = "Invalid username or password";
}
}

View File

@ -0,0 +1,12 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace isnd.Data
{
public class ExternalProvider
{
public string DisplayName { get; set; }
public string AuthenticationScheme { get; set; }
}
}

View File

@ -0,0 +1,19 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace isnd.Data
{
public class LoggedOutViewModel
{
public string PostLogoutRedirectUri { get; set; }
public string ClientName { get; set; }
public string SignOutIframeUrl { get; set; }
public bool AutomaticRedirectAfterSignOut { get; set; }
public string LogoutId { get; set; }
public bool TriggerExternalSignout => ExternalAuthenticationScheme != null;
public string ExternalAuthenticationScheme { get; set; }
}
}

View File

@ -0,0 +1,18 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
using System.ComponentModel.DataAnnotations;
namespace isnd.Data
{
public class LoginInputModel
{
[Required]
public string Username { get; set; }
[Required]
public string Password { get; set; }
public bool RememberLogin { get; set; }
public string ReturnUrl { get; set; }
}
}

View File

@ -0,0 +1,22 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
namespace isnd.Data
{
public class LoginViewModel : LoginInputModel
{
public bool AllowRememberLogin { get; set; } = true;
public bool EnableLocalLogin { get; set; } = true;
public IEnumerable<ExternalProvider> ExternalProviders { get; set; } = Enumerable.Empty<ExternalProvider>();
public IEnumerable<ExternalProvider> VisibleExternalProviders => ExternalProviders.Where(x => !String.IsNullOrWhiteSpace(x.DisplayName));
public bool IsExternalLoginOnly => EnableLocalLogin == false && ExternalProviders?.Count() == 1;
public string ExternalLoginScheme => IsExternalLoginOnly ? ExternalProviders?.SingleOrDefault()?.AuthenticationScheme : null;
}
}

View File

@ -0,0 +1,11 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace isnd.Data
{
public class LogoutInputModel
{
public string LogoutId { get; set; }
}
}

View File

@ -0,0 +1,11 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace isnd.Data
{
public class LogoutViewModel : LogoutInputModel
{
public bool ShowLogoutPrompt { get; set; } = true;
}
}

View File

@ -0,0 +1,12 @@
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
namespace isnd.Data
{
public class RedirectViewModel
{
public string RedirectUrl { get; set; }
}
}

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace isnd.Data
{
public class RegisterViewModel
{
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 2)]
[Display(Name = "FullName")]
public string FullName { get; set; }
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
}

View File

@ -0,0 +1,25 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace isnd.Data.ApiKeys
{
public class ApiKey
{
[Required][Key][DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Id { get; set; }
[Required][ForeignKey("User")]
public string UserId { get; set; }
public virtual ApplicationUser User { get; set; }
public string Name { get; set; }
public int ValidityPeriodInDays{ get; set; }
public DateTimeOffset CreationDate { get; set; }
public bool IsValid => CreationDate.AddDays(ValidityPeriodInDays) > DateTime.Now;
}
}

View File

@ -0,0 +1,14 @@
using System.ComponentModel.DataAnnotations;
namespace isnd.Data.ApiKeys
{
public class ApiKeyViewModel
{
[Display(Name = "Key Name")]
public string Name { get; set; }
[Display(Name = "Key Value")]
public string ProtectedValue { get; set; }
}
}

View File

@ -0,0 +1,16 @@
using System.ComponentModel.DataAnnotations;
namespace isnd.Data.ApiKeys
{
public class CreateModel
{
[Required][StringLength(255)]
[Display(Name = "Key Name")]
public string Name { get; set; }
public string UserId { get; set; }
public int ValidityPeriodInDays { get; set; }
}
}

View File

@ -0,0 +1,7 @@
namespace isnd.Data.ApiKeys
{
public class DeleteModel
{
public ApiKey ApiKey { get; set; }
}
}

View File

@ -0,0 +1,8 @@
namespace isnd.Data.ApiKeys
{
public class DetailModel : ApiKeyViewModel
{
public ApiKey ApiKey { get; set; }
}
}

View File

@ -0,0 +1,12 @@
namespace isnd.Data.ApiKeys
{
public class EditModel
{
public EditModel()
{
if (ApiKey==null) ApiKey = new ApiKey();
}
public ApiKey ApiKey { get; set; }
}
}

View File

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace isnd.Data.ApiKeys
{
public class IndexModel
{
public List<ApiKey> ApiKey { get; set; }
}
}

View File

@ -0,0 +1,63 @@
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using isnd.Data.ApiKeys;
using isnd.Data.Packages;
using Microsoft.VisualStudio.Web.CodeGenerators.Mvc.Templates.BlazorIdentity.Pages;
using Microsoft.DotNet.Scaffolding.Shared.ProjectModel;
namespace isnd.Data
{
/// <summary>
/// Application Db Context
/// </summary>
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
/// <summary>
/// db context ctor
/// </summary>
/// <param name="options"></param>
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options) { }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
_ = builder.Entity<PackageVersion>()
.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 } );
}
/// <summary>
/// User API keys
/// </summary>
/// <value></value>
public DbSet<ApiKey> ApiKeys { get; set; }
/// <summary>
/// Packages
/// </summary>
/// <value></value>
public DbSet<Package> Packages { get; set; }
/// <summary>
/// Package Versions
/// </summary>
/// <value></value>
public DbSet<PackageVersion> PackageVersions { get; set; }
/// <summary>
/// Commits
/// </summary>
/// <value></value>
public DbSet<Commit> Commits { get; set; }
public DbSet<Dependency> Dependencies { get; set; }
public DbSet<PackageDependencyGroup> PackageDependencyGroups { get; set; }
}
}

View File

@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Identity;
namespace isnd.Data
{
// Add profile data for application users by adding properties to the ApplicationUser class
public class ApplicationUser : IdentityUser
{
[PersonalData]
public string FullName { get; set; }
}
}

View File

@ -0,0 +1,11 @@
using NuGet.Versioning;
namespace isnd.Data.Catalog
{
public class AlternatePackage
{
public string id { get ; set; }
public VersionRange range { get ; set; }
}
}

View File

@ -0,0 +1,14 @@
namespace isnd.Data.Catalog
{
public class Deprecation
{
/*
Legacy The package is no longer maintained
CriticalBugs The package has bugs which make it unsuitable for usage
Other The package is deprecated due to a reason not on this list
*/
public string[] reasons { get; set; } // array of strings yes The reasons why the package was deprecated
public string message { get; set; } // The additional details about this deprecation
public AlternatePackage alternatePackage { get; set; } // object no The alternate package that should be used instead
}
}

View File

@ -0,0 +1,161 @@
using System.ComponentModel;
using System.Net.Sockets;
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
using isnd.Data.Packages;
using NuGet.Versioning;
using System.Collections.Generic;
using System;
using isnd.Interfaces;
using NuGet.Protocol.Core.Types;
using NuGet.Protocol;
using NuGet.Packaging;
using NuGet.Packaging.Core;
using System.Threading.Tasks;
using isnd.Entities;
namespace isnd.Data.Catalog
{
public class PackageDetails : Permalink, IObject
{
/// <summary>
/// Creates a catalog entry
/// </summary>
/// <param name="pkg">package id</param>
/// <param name="apiBase">api Base</param>
/// <returns></returns>
public PackageDetails(PackageVersion pkg, string apiBase): base( apiBase + ApiConfig.Package + "/" + pkg.PackageId + "/" + pkg.FullString)
{
Title = PackageId = pkg.Package.Id;
Version = pkg.FullString;
Authors = $"{pkg.Package.Owner.FullName} <${pkg.Package.Owner.Email}>";
packageContent = apiBase + pkg.NugetLink;
CommitId = pkg.CommitId;
Published = CommitTimeStamp = pkg.LatestCommit.CommitTimeStamp;
Listed = !pkg.IsDeleted && pkg.Package.Public;
if (pkg.DependencyGroups!=null)
{
DependencySets = pkg.DependencyGroups;
}
Description = pkg.Description;
Owners = pkg.Package.Owner.Email;
// TODO Licence Project Urls, Summary, Title, Owners, etc ...
}
[JsonProperty("@type")]
public string[] RefType { get; protected set; } = new string[] { "PackageDetails" };
[JsonProperty("commitId")]
public string CommitId { get; set; }
[JsonProperty("commitTimeStamp")]
public DateTimeOffset CommitTimeStamp { get; set; }
/// <summary>
/// Authors
/// </summary>
/// <value>string or array of strings</value>
[JsonProperty("authors")]
public string Authors { get; set; }
/// <summary>
/// The deprecation associated with the package
/// </summary>
/// <value></value>
public Deprecation deprecation { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("iconUrl")]
public Uri IconUrl { get; set; }
public string language { get; set; }
[JsonProperty("licenseUrl")]
public Uri LicenseUrl { get; set; }
public string licenseExpression { get; set; }
public string minClientVersion { get; set; }
[JsonProperty("projectUrl")]
public Uri ProjectUrl { get; set; }
[JsonProperty("requireLicenseAcceptance")]
public bool RequireLicenseAcceptance { get; set; }
[JsonProperty("summary")]
public string Summary { get; set; }
/// <summary>
/// The tags
/// </summary>
/// <value></value>
[JsonProperty("tags")]
public string Tags { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
/// <summary>
/// The security vulnerabilities of the package
/// </summary>
/// <value></value>
public IEnumerable<PackageVulnerabilityMetadata> Vulnerabilities { get; }
public string packageContent { get; set; }
/// <summary>
/// A string containing a ISO 8601 timestamp of when the package was published
/// </summary>
/// <value></value>
[JsonProperty("published")]
public DateTimeOffset? Published { get; set; }
/// <summary>
/// The full version string after normalization
/// </summary>
/// <value></value>
[Required,JsonRequired]
[JsonProperty("version")]
public string Version { get; set; }
[Required,JsonRequired]
[JsonProperty("id")]
public string PackageId { get; set; }
public long? DownloadCount { get; set; }
public Uri ReadmeUrl { get; set; }
public Uri ReportAbuseUrl { get; set; }
public string Owners { get; set; }
public bool Listed { get; set; }
public LicenseMetadata LicenseMetadata { get; set; }
[JsonProperty("dependencyGroups")]
public IEnumerable<PackageDependencyGroup> DependencySets {get; set;}
public Task<PackageDeprecationMetadata> GetDeprecationMetadataAsync()
{
throw new NotImplementedException();
}
public Task<IEnumerable<VersionInfo>> GetVersionsAsync()
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,33 @@
using isnd.Data.Packages;
using isnd.Entities;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
namespace isnd.Data.Catalog
{
public class Catalog : Permalink
{
public Catalog(string url) : base(url)
{
Items = new List<RegistrationPage>();
}
public Catalog(string apiBase, string pkgId, IEnumerable<PackageVersion> versions) : base($"{apiBase}{ApiConfig.Registration}/{pkgId}/index.json")
{
Items = new List<RegistrationPage>
{
new RegistrationPage(pkgId, apiBase, versions)
};
}
[JsonProperty("count")]
public int Count { get => Items.Count; }
[JsonProperty("items")]
public List<RegistrationPage> Items { get; set; }
}
}

View File

@ -0,0 +1,25 @@
using System.Collections.Generic;
using isnd.Data.Packages;
using Newtonsoft.Json;
namespace isnd.Data.Catalog
{
public class PackageRegistrationQuery
{
public PackageRegistrationQuery()
{
}
[JsonProperty("query")]
public string Query { get; set; }
[JsonProperty("prerelease")]
public bool Prerelease { get; set; } = true;
[JsonProperty("skip")]
public int Skip { get; set; } = 0;
[JsonProperty("take")]
public int Take { get; set; } = 25;
}
}

View File

@ -0,0 +1,54 @@
using System.ComponentModel.DataAnnotations;
using isnd.Entities;
using Newtonsoft.Json;
using isn.abst;
namespace isnd.Data.Catalog
{
/// <summary>
/// Hosts a catalog entry,
/// the atomic content reference
/// </summary>
public class RegistrationLeaf
{
public RegistrationLeaf(string apiBase, string pkgId, string fullVersionString, PackageDetails entry)
{
this.registration = apiBase + ApiConfig.Registration + "/" + pkgId + "/" + fullVersionString + ".json";
Id = registration;
this.PackageContent = apiBase + ApiConfig.ContentBase + "/" + pkgId + "/" + fullVersionString
+ "/" + pkgId + "-" + fullVersionString + "." + Constants.PacketFileExtension;
Entry = entry;
}
/*
@id string yes
catalogEntry object yes
packageContent string yes
*/
/// <summary>
/// The URL to the registration leaf
/// </summary>
/// <value></value>
[JsonProperty("@id")]
[Key][Required]
[StringLength(1024)]
public string Id { get; set; }
/// <summary>
/// The catalog entry containing the package metadata
/// </summary>
/// <value></value>
[JsonProperty("catalogEntry")]
public PackageDetails Entry { get; set; }
/// <summary>
/// The URL to the package content (.nupkg)
/// </summary>
/// <value></value>
[JsonProperty("packageContent")]
public string PackageContent { get; set; }
public string registration { get; set; }
}
}

View File

@ -0,0 +1,100 @@
using System;
using System.Collections.Generic;
using System.Linq;
using isnd.Data.Packages;
using Newtonsoft.Json;
using NuGet.Versioning;
namespace isnd.Data.Catalog
{
public class RegistrationPage : Permalink
{
private readonly string pkgid;
private readonly List<RegistrationLeaf> items;
private readonly string apiBase;
public RegistrationPage (string pkgid, string apiBase) : base(apiBase + $"/registration/{pkgid}/index.json")
{
Parent = apiBase + $"/registration/{pkgid}/index.json";
this.items = new List<RegistrationLeaf>();
this.pkgid = pkgid;
this.apiBase = apiBase;
}
public RegistrationPage(string pkgid, string apiBase, IEnumerable<PackageVersion> versions) : this(pkgid, apiBase)
{
AddVersionRange(versions);
}
public string GetPackageId()
{
return pkgid;
}
/// <summary>
/// The array of registration leaves and their associate metadata
/// </summary>
/// <value></value>
[JsonProperty("items")]
public RegistrationLeaf[] Items { get => items.ToArray(); }
public void AddVersionRange(IEnumerable<PackageVersion> vitems)
{
if (vitems.Count() == 0) return;
NuGetVersion upper = null;
NuGetVersion lower = null;
PackageVersion latest = null;
if (Lower!=null) lower = new NuGetVersion(Lower);
if (Upper!=null) upper = new NuGetVersion(Upper);
// Assert.True(items.All(p=>p.Id == id));
long commitMax = 0;
foreach (var p in vitems)
{
if (p.LatestCommit==null) continue;
var pkg = p.ToPackage(apiBase);
if (items.Contains(pkg)) continue;
if (upper == null) upper = p.NugetVersion;
else if ( upper < p.NugetVersion) upper = p.NugetVersion;
if (lower == null) lower = p.NugetVersion;
else if (lower > p.NugetVersion) lower = p.NugetVersion;
if (p.CommitNId > commitMax)
{
latest = p;
}
items.Add(pkg);
}
Upper = upper?.ToFullString() ?? lower?.ToFullString();
Lower = lower?.ToFullString() ?? upper?.ToFullString();
}
/// <summary>
/// The highest SemVer 2.0.0 version in the page (inclusive)
/// </summary>
/// <value></value>
[JsonProperty("upper"), JsonRequired]
public string Upper { get; private set; }
/// <summary>
/// The lowest SemVer 2.0.0 version in the page (inclusive)
/// </summary>
/// <value></value>
[JsonProperty("lower"), JsonRequired]
public string Lower { get; private set; }
/// <summary>
/// The URL to the registration index
/// </summary>
/// <value></value>
[JsonProperty("parent")]
public string Parent { get; set; }
[JsonProperty("count")]
public int Count { get => items.Count; }
}
}

View File

@ -0,0 +1,9 @@
namespace isnd.Data.Catalog
{
public class Vulnerabilitie
{
public string advisoryUrl { get; set; } // string yes Location of security advisory for the package
public string severity { get; set; } // string yes Severity of advisory: "0" = Low, "1" = Moderate, "2" = High, "3" = Critical
}
}

View File

@ -0,0 +1,31 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using isnd.Data.Packages;
using isnd.Data.Packages.Catalog;
namespace isnd.Data.Historic
{
public class PackageVersionCommit
{
public long CommitId { get; set; }
[StringLength(1024)]
public string PackageId { get; set; }
[StringLength(256)]
public string FullString { get; set; }
[StringLength(256)]
public string PackageType { get; set; }
[ForeignKey("CommitId")]
public virtual Commit Commit { get; set; }
[ForeignKey("PackageId")]
public virtual Package Package { get; set; }
[ForeignKey("PackageId,FullString,PackageType")]
public virtual PackageVersion PackageVersion { get; set; }
}
}

View File

@ -0,0 +1,18 @@
using System;
using Newtonsoft.Json;
namespace isnd.Interfaces
{
public interface IObject
{
[JsonProperty("@type")]
public string Type { get => GetType().Name; }
[JsonProperty("commitId")]
string CommitId { get; }
[JsonProperty("commitTimeStamp")]
DateTimeOffset CommitTimeStamp { get; }
}
}

View File

@ -0,0 +1,52 @@
using System;
using System.ComponentModel.DataAnnotations.Schema;
using isnd.Interfaces;
using Newtonsoft.Json;
namespace isnd.Data.Packages.Catalog
{
/// <summary>
/// An presence of package in a catalog,
/// for availability, or deletion,
///
/// </summary>
public class PackageDetails : IObject
{
[JsonProperty("@id")]
public string RefId { get; set; }
/// <summary>
/// Reference type :
/// nuget:PackageDetails vs nuget:PackageDelete
/// </summary>
/// <value></value>
[JsonProperty("@type")]
public string RefType { get; set; }
/// <summary>
/// The NuGet Id
/// </summary>
/// <value></value>
[JsonProperty("nuget:id")]
public string Id { get; set; }
/// <summary>
/// The NuGet version
/// </summary>
/// <value></value>
[JsonProperty("nuget:version")]
public string Version { get; set; }
[JsonProperty("commitId")]
public string CommitId { get; set; }
[ForeignKey("CommitId"), JsonIgnore]
public virtual Commit LastCommit { get; set; }
[JsonProperty("commitTimeStamp")]
public DateTimeOffset CommitTimeStamp { get; set; }
}
}

View File

@ -0,0 +1,12 @@
using System;
using isnd.Interfaces;
namespace isnd.Data.Packages.Catalog
{
public class PackageEntry : IObject
{
public string CommitId => throw new NotImplementedException();
public DateTimeOffset CommitTimeStamp => throw new NotImplementedException();
}
}

View File

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Newtonsoft.Json;
using isnd.Interfaces;
namespace isnd.Data.Packages
{
public enum PackageAction
{
DeletePackage,
PublishPackage
}
public class Commit : IObject
{
[Key,
DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[JsonIgnore]
public long Id { get; set; }
[Required][JsonIgnore]
public DateTimeOffset TimeStamp{ get; set; }
[Required]
public PackageAction Action { get; set; }
[NotMapped]
public string CommitId { get => Id.ToString(); }
[NotMapped]
public DateTimeOffset CommitTimeStamp { get => TimeStamp; }
[ForeignKey("CommitNId")]
public virtual List<PackageVersion> Versions { get; set; }
}
}

View File

@ -0,0 +1,42 @@
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
{
[JsonIgnore]
[Key][DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Id { get;set;}
/// <summary>
/// Dependency Package Identifier
/// </summary>
/// <value></value>
[JsonProperty("id")]
public string PackageId { get;set;}
/// <summary>
/// Dependency Group Id
/// </summary>
/// <value></value>
[Required]
[ForeignKey("Group")]
[JsonIgnore]
public string DependencyGroupId { set ; get ; }
[JsonIgnore]
public virtual PackageDependencyGroup Group{ get; set; }
// TODO Version Range
[JsonProperty("range")][StringLength(1024)]
public string Version { get; set; }
[JsonProperty("exclude")][StringLength(2048)]
public string Exclude { get; set; }
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
namespace isnd.Data.Packages
{
public interface IPackage
{
string Id { get; set; }
string OwnerId { get; set; }
string Description { get; set; }
bool Public { get; set; }
ApplicationUser Owner { get; set; }
List<PackageVersion> Versions { get; set; }
long CommitNId { get; set; }
string CommitId { get; }
Commit LatestCommit { get; set; }
DateTimeOffset CommitTimeStamp { get; set; }
}
}

Some files were not shown because too many files have changed in this diff Show More