No release yet
parent
3b7e286a5b
commit
58d1f26701
@ -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>
|
@ -1,3 +1,26 @@
|
||||
.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/
|
||||
/packages/
|
||||
/bin/
|
||||
/obj/
|
||||
bower_components/
|
||||
/test/isn.tests/bin
|
||||
/test/isn.tests/obj/
|
||||
appsettings.Testing.json
|
||||
appsettings.Development.json
|
||||
/wwwroot/.sass-cache/
|
||||
/src/isnd/wwwroot/.sass-cache/
|
||||
/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/
|
||||
|
||||
|
@ -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
|
@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
mode: Mainline
|
||||
branches: {}
|
||||
ignore:
|
||||
sha: []
|
||||
merge-message-formats: {}
|
@ -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.
|
||||
|
@ -0,0 +1,25 @@
|
||||
|
||||
CONFIGURATION=Debug
|
||||
TARGETFV=net7.0
|
||||
VERSION=1.0.24
|
||||
|
||||
all: build-isn build-isnd
|
||||
|
||||
build-%: src/%
|
||||
dotnet build -p:Configuration=$(CONFIGURATION) $^
|
||||
|
||||
pack-%: src/%
|
||||
dotnet pack $^
|
||||
|
||||
watch:
|
||||
dotnet watch --project=src/isnd
|
||||
|
||||
push-%: src/%
|
||||
isn push $^/bin/$(CONFIGURATION)/$^.$(VERSION).nupkg
|
||||
|
||||
clean-%: src/%
|
||||
rm -rf $^/bin $^/obj
|
||||
|
||||
clean: clean-isnd clean-isn clean-isn.abst
|
||||
|
||||
|
@ -1,11 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace nuget_host.Models
|
||||
{
|
||||
public class ErrorViewModel
|
||||
{
|
||||
public string RequestId { get; set; }
|
||||
|
||||
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
|
||||
}
|
||||
}
|
@ -1,9 +1,89 @@
|
||||
# Usage
|
||||
# ISN
|
||||
|
||||
En cours de developement, le détail du paquet n'est toujours pas fourni en API.
|
||||
|
||||
## 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
|
||||
|
||||
Depuis le dossier de la solution, compiler la solution :
|
||||
|
||||
````bash
|
||||
dotnet build /restore -c Release
|
||||
dotnet publish -c Release
|
||||
````
|
||||
|
||||
### Déploiement du serveur
|
||||
|
||||
Commençont par la livraison initiale, aujourd'hui :
|
||||
|
||||
````bash
|
||||
sudo mkdir -p /srv/www/isnd
|
||||
sudo cp -a src/isnd/bin/Release/net7.0/publish/* /srv/www/isnd
|
||||
sudo cp contrib/isnd.service /etc/systemd/system
|
||||
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 :
|
||||
|
||||
* la connextion à une base de donnée Postresgql, sous la forme :
|
||||
`"Server=<pgserver>;Port=<pgport>;Database=<dbname>;Username=<dbusername>;Password=<dbpass>;"`
|
||||
* la connection au serveur de messagerie,
|
||||
* l'URL du ou des sites à propulser,
|
||||
* 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
|
||||
|
||||
````bash
|
||||
# compiler tout
|
||||
dotnet build -c Release
|
||||
dotnet publish -c Release -f net7.0 src/isnd
|
||||
# MAJ du serveur
|
||||
sudo systemctl stop isnd
|
||||
sudo cp -a src/isnd/bin/Release/net7.0/publish/* /srv/www/isnd
|
||||
sudo systemctl start isnd
|
||||
# MAJ du client
|
||||
sudo cp -a src/isn/bin/Release/net7.0/* /usr/local/lib/isn
|
||||
sudo chown -R root.root /usr/local/lib/isn
|
||||
````
|
||||
|
@ -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 & 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>
|
@ -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>
|
@ -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>© 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>
|
@ -1,3 +0,0 @@
|
||||
@using nuget_host
|
||||
@using nuget_host.Models
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
@ -0,0 +1,28 @@
|
||||
[Unit]
|
||||
Description=isnd - a Nuget package repository daemon
|
||||
After=syslog.target
|
||||
After=network.target
|
||||
|
||||
Wants=postgresql.service
|
||||
After=postgresql.service
|
||||
|
||||
[Service]
|
||||
RestartSec=5s
|
||||
Type=simple
|
||||
User=isn
|
||||
Group=isn
|
||||
WorkingDirectory=/srv/www/isnd/
|
||||
# If using Unix socket: tells systemd to create the /run/gitea folder, which will contain the gitea.sock file
|
||||
# (manually creating /run/gitea doesn't work, because it would not persist across reboots)
|
||||
#RuntimeDirectory=gitea
|
||||
ExecStart=/srv/www/isnd/isnd
|
||||
Restart=always
|
||||
Environment=HOME=/srv/www/isnd
|
||||
|
||||
#CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
|
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<apikeys>
|
||||
</apikeys>
|
||||
<packageSources>
|
||||
<add key="myIsnDev" value="http://localhost:5000/index.json" protocolVersion="3" />
|
||||
</packageSources>
|
||||
</configuration>
|
@ -0,0 +1,2 @@
|
||||
nuget install -Verbosity detailed -Source http://localhost:5000/index.json -Prerelease Yavsc.Abstract
|
||||
nuget locals all -clear
|
@ -0,0 +1,4 @@
|
||||
= URL's
|
||||
|
||||
<http://localhost:5000/v3.4.0/registration/yavsc.abstract/index.json>
|
||||
|
@ -0,0 +1,30 @@
|
||||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"codecov.tool": {
|
||||
"version": "1.13.0",
|
||||
"commands": [
|
||||
"codecov"
|
||||
]
|
||||
},
|
||||
"gitversion.tool": {
|
||||
"version": "5.10.1",
|
||||
"commands": [
|
||||
"dotnet-gitversion"
|
||||
]
|
||||
},
|
||||
"gitreleasemanager.tool": {
|
||||
"version": "0.13.0",
|
||||
"commands": [
|
||||
"dotnet-gitreleasemanager"
|
||||
]
|
||||
},
|
||||
"Wyam2.Tool": {
|
||||
"version": "3.0.0-rc3",
|
||||
"commands": [
|
||||
"wyam2"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
{
|
||||
"dotnet": {
|
||||
"enabled": false
|
||||
},
|
||||
"msbuild": {
|
||||
"enabled": true
|
||||
},
|
||||
"Dnx": {
|
||||
"enabled": false
|
||||
},
|
||||
"Script": {
|
||||
"enabled": false
|
||||
},
|
||||
"fileOptions": {
|
||||
"systemExcludeSearchPatterns": [
|
||||
"**/bin/**/*",
|
||||
"**/obj/**/*",
|
||||
"**/node_modules/**/*"
|
||||
],
|
||||
"userExcludeSearchPatterns": []
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
using isn.abst;
|
||||
|
||||
namespace isnd.Entities
|
||||
{
|
||||
public static class ApiConfig
|
||||
{
|
||||
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 Nuspec = "/nuspec";
|
||||
public const string Nuget = "/nuget";
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
using isnd.Data.Catalog;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace isn.Abstract
|
||||
{
|
||||
public class ApiIndexViewModel : HappyIdOwner
|
||||
{
|
||||
public ApiIndexViewModel(string id) : base(id)
|
||||
{
|
||||
}
|
||||
|
||||
[JsonProperty("@id")]
|
||||
public string Id { get => GetId(); }
|
||||
|
||||
[JsonProperty("version")]
|
||||
public string Version { get; set; }
|
||||
|
||||
[JsonProperty("resources")]
|
||||
public Resource[] Resources { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
namespace isn.abst
|
||||
{
|
||||
public static class Constants
|
||||
{
|
||||
public const string PaquetFileEstension = "nupkg";
|
||||
public const string SpecFileEstension = "nuspec";
|
||||
public const string ApiVersion = "/v3";
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace isnd.Data.Catalog
|
||||
{
|
||||
public class HappyIdOwner
|
||||
{
|
||||
public HappyIdOwner(string id)
|
||||
{
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
|
||||
private string id;
|
||||
|
||||
public string GetId() { return id; }
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj!=null)
|
||||
{
|
||||
if (GetType().IsAssignableFrom(obj.GetType()))
|
||||
{
|
||||
var rpobj = (HappyIdOwner) obj;
|
||||
return this.id == rpobj.id;
|
||||
}
|
||||
}
|
||||
return base.Equals(obj);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return id.GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
using isnd.Data.Catalog;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace isn.Abstract
|
||||
{
|
||||
public class Resource : HappyIdOwner
|
||||
{
|
||||
public Resource(string id, string typename) : base(id)
|
||||
{
|
||||
Type = typename;
|
||||
Id = id;
|
||||
}
|
||||
|
||||
[JsonProperty("@id")]
|
||||
public string Id {get; set; }
|
||||
|
||||
[JsonProperty("@type")]
|
||||
public string Type {get; set; }
|
||||
|
||||
[JsonProperty("comment")]
|
||||
public string Comment {get; set; }
|
||||
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
namespace isn
|
||||
{
|
||||
internal static class Constants
|
||||
{
|
||||
internal const string ClientVersion = "isn v1.0";
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace isn
|
||||
{
|
||||
public interface IDataProtector
|
||||
{
|
||||
string Protect(string data);
|
||||
string UnProtect(string data);
|
||||
}
|
||||
|
||||
public class DefaultDataProtector : IDataProtector
|
||||
{
|
||||
private byte delta = 145;
|
||||
|
||||
public DefaultDataProtector()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public string Protect(string data)
|
||||
{
|
||||
List<Byte> protd = new List<byte>();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (byte c in Encoding.UTF8.GetBytes(data))
|
||||
{
|
||||
protd.Add((byte) (c ^ delta));
|
||||
}
|
||||
return System.Convert.ToBase64String(protd.ToArray());
|
||||
}
|
||||
|
||||
public string UnProtect(string data)
|
||||
{
|
||||
if (data==null) return null;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
List<byte> unps = new List<byte>();
|
||||
|
||||
foreach (byte c in System.Convert.FromBase64CharArray(data.ToCharArray(),0,data.Length))
|
||||
{
|
||||
unps.Add((byte) (c ^ delta));
|
||||
}
|
||||
return Encoding.UTF8.GetString(unps.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
namespace isn
|
||||
{
|
||||
public class IsnSourceSettings
|
||||
{
|
||||
internal string Source { get; set; }
|
||||
|
||||
internal string[] Keys { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
namespace isn
|
||||
{
|
||||
public class IsndErrorMessage
|
||||
{
|
||||
public int ecode { get; set; }
|
||||
public string msg { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace isn
|
||||
{
|
||||
public class SourceSettings
|
||||
{
|
||||
public string ApiKey { get; set; }
|
||||
public string Alias { get; set; }
|
||||
|
||||
public string GetClearApiKey()
|
||||
{
|
||||
return Protector.UnProtect(ApiKey);
|
||||
}
|
||||
public void SetApiKey(string key)
|
||||
{
|
||||
ApiKey = Protector.Protect(key);
|
||||
}
|
||||
|
||||
public static IDataProtector Protector { get; set; } = new DefaultDataProtector();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class Settings
|
||||
{
|
||||
public string DataProtectionTitle { get; set; }
|
||||
public Dictionary<string, SourceSettings> Sources { get; set; }
|
||||
|
||||
public bool AutoUpdateApiKey { get; set; } = false;
|
||||
|
||||
private string defSourceKey;
|
||||
|
||||
/// <summary>
|
||||
/// Default source by its alias
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public string DefaultSourceKey
|
||||
{
|
||||
get => defSourceKey;
|
||||
set
|
||||
{
|
||||
if (!Sources.ContainsKey(value))
|
||||
{
|
||||
Sources[value]=new SourceSettings
|
||||
{
|
||||
Alias = defSourceKey
|
||||
};
|
||||
}
|
||||
defSourceKey = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
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 ()=> {
|
||||
var response = await client.GetStringAsync(url);
|
||||
result = JsonConvert.DeserializeObject<ApiIndexViewModel>(response);
|
||||
}).Wait();
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
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())
|
||||
{
|
||||
/* var values = new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("Id", Guid.NewGuid().ToString()),
|
||||
new KeyValuePair<string, string>("Key", "awesome"),
|
||||
new KeyValuePair<string, string>("From", "khalid@home.com")
|
||||
//other values
|
||||
};foreach (var keyValuePair in values)
|
||||
{
|
||||
multipartFormDataContent.Add(new StringContent(keyValuePair.Value),
|
||||
String.Format("\"{0}\"", keyValuePair.Key));
|
||||
} */
|
||||
multipartFormDataContent.Add(new ByteArrayContent(File.ReadAllBytes(fi.FullName)),
|
||||
'"' + "File" + '"',
|
||||
'"' + fi.Name + '"');
|
||||
|
||||
var result = await client.PutAsync(uri, multipartFormDataContent);
|
||||
|
||||
if (result.IsSuccessStatusCode)
|
||||
{
|
||||
string report = await result.Content.ReadAsStringAsync();
|
||||
Console.WriteLine(report);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
string ereport = await result.Content.ReadAsStringAsync();
|
||||
Console.WriteLine(ereport);
|
||||
}
|
||||
return new PushReport();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
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 static class PushCommand
|
||||
{
|
||||
static public PushReport Run(string pkg, string source, string apikey)
|
||||
{
|
||||
if (source == null) source = Program.Settings.DefaultSourceKey;
|
||||
if (source == null) throw new InvalidOperationException("source is null");
|
||||
if (Program.Settings.Sources.Values.Any(s=>s.Alias==source))
|
||||
source = Program.Settings.Sources.First(s=>s.Value.Alias==source).Key;
|
||||
if (source==null) throw new InvalidOperationException("source is invalid");
|
||||
|
||||
var resources = SourceHelpers.GetServerResources(source);
|
||||
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 = "Le fichier n'existe pas"
|
||||
};
|
||||
return report;
|
||||
}
|
||||
using (var client = new HttpClient())
|
||||
try
|
||||
{
|
||||
Console.WriteLine("Connecting to "+ pubRes.Id);
|
||||
return client.UploadFilesToServer(new Uri(pubRes.Id), fi, apikey);
|
||||
}
|
||||
catch (HttpRequestException hrex)
|
||||
{
|
||||
var report = new PushReport
|
||||
{
|
||||
PkgName = fi.Name,
|
||||
Message = "HttpRequest: " + hrex.Message,
|
||||
StackTrace = hrex.StackTrace,
|
||||
StatusCode = hrex.HResult.ToString()
|
||||
};
|
||||
Console.Error.WriteLine(hrex.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
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>();
|
||||
|
||||
foreach (string pkg in pkgs)
|
||||
{
|
||||
var report = PushCommand.Run(pkg, CurrentSource, apiKey);
|
||||
Console.WriteLine(report.ToDoc());
|
||||
pushReports.Add(report);
|
||||
}
|
||||
if (storApiKey)
|
||||
{
|
||||
EnsureKeyStored();
|
||||
}
|
||||
return pushReports;
|
||||
}
|
||||
|
||||
private static object Add(IEnumerable<string> str)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
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 (shouldShowPushHelp)
|
||||
{
|
||||
// output the options
|
||||
Console.Error.WriteLine("Push Options:");
|
||||
storeoptions.WriteOptionDescriptions(Console.Out);
|
||||
}
|
||||
else
|
||||
{
|
||||
apiKey = args[0];
|
||||
EnsureKeyStored();
|
||||
}
|
||||
}
|
||||
|
||||
public static void EnsureKeyStored()
|
||||
{
|
||||
if (CurrentSource == null)
|
||||
{
|
||||
if (Settings.DefaultSourceKey == null)
|
||||
return;
|
||||
CurrentSource = Settings.DefaultSourceKey;
|
||||
}
|
||||
|
||||
if (Settings.Sources.ContainsKey(CurrentSource))
|
||||
{
|
||||
if (apiKey == null)
|
||||
{
|
||||
// Une suppression
|
||||
Settings.Sources.Remove(CurrentSource);
|
||||
if (Settings.DefaultSourceKey == CurrentSource) Settings.DefaultSourceKey = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Une mise À jour
|
||||
Settings.Sources[CurrentSource].SetApiKey(apiKey);
|
||||
if (Settings.DefaultSourceKey == null) Settings.DefaultSourceKey = CurrentSource;
|
||||
}
|
||||
}
|
||||
else if (apiKey != null)
|
||||
{
|
||||
// une addition
|
||||
var setting = new SourceSettings ();
|
||||
setting.SetApiKey(apiKey);
|
||||
Settings.Sources.Add(CurrentSource, setting);
|
||||
}
|
||||
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
|
||||
));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -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));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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.Alias);
|
||||
}
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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.Alias == arg) ;
|
||||
if (settings==null) throw new InvalidOperationException(arg);
|
||||
Settings.DefaultSourceKey = arg;
|
||||
SaveConfig();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
namespace isn
|
||||
{
|
||||
public class PushReport
|
||||
{
|
||||
public string PkgName { get; set; }
|
||||
public bool Executed { get; internal set; }
|
||||
public bool OK { get; internal set; }
|
||||
public bool AlreadyPresent { get; internal set; }
|
||||
public string Message { get; internal set; }
|
||||
public string StatusCode { get; internal set; }
|
||||
public string StackTrace { get; internal set; }
|
||||
|
||||
public string ToDoc()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder($"= Pushing {PkgName}\n\n");
|
||||
if (Executed) sb.AppendLine("* Executed");
|
||||
if (OK) sb.AppendLine("* OK");
|
||||
if (!string.IsNullOrWhiteSpace(Message))
|
||||
sb.AppendLine(Message);
|
||||
if (!string.IsNullOrWhiteSpace(StatusCode))
|
||||
sb.AppendLine($"* Status Code : ");
|
||||
if (!string.IsNullOrWhiteSpace(StackTrace))
|
||||
sb.AppendLine($"* StackTrace : ");
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace isnd.Authorization
|
||||
{
|
||||
internal class ValidApiKeyRequirement : IAuthorizationRequirement
|
||||
{
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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";
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,145 @@
|
||||
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;
|
||||
|
||||
|
||||
namespace isnd.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
public class ApiKeysController : Controller
|
||||
{
|
||||
private readonly ApplicationDbContext dbContext;
|
||||
private readonly IsndSettings isndSettings;
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
|
||||
private readonly IDataProtector protector;
|
||||
public ApiKeysController(ApplicationDbContext dbContext,
|
||||
IOptions<IsndSettings> isndSettingsOptions,
|
||||
IDataProtectionProvider provider,
|
||||
UserManager<ApplicationUser> userManager)
|
||||
{
|
||||
this.dbContext = dbContext;
|
||||
this.isndSettings = isndSettingsOptions.Value;
|
||||
protector = provider.CreateProtector(isndSettings.ProtectionTitle);
|
||||
_userManager = userManager;
|
||||
}
|
||||
|
||||
[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 = GetUserKeys();
|
||||
if (userKeys.Count() >= isndSettings.MaxUserKeyCount)
|
||||
{
|
||||
ModelState.AddModelError(null, "Maximum key count reached");
|
||||
return View();
|
||||
}
|
||||
ApiKey newKey = new ApiKey { UserId = userid, Name = model.Name,
|
||||
CreationDate = DateTime.Now };
|
||||
_ = dbContext.ApiKeys.Add(newKey);
|
||||
_ = await dbContext.SaveChangesAsync();
|
||||
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(null, "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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using isnd.Entities;
|
||||
using isnd.Services;
|
||||
using isn.Abstract;
|
||||
using isn.abst;
|
||||
using isnd.Interfaces;
|
||||
using Unleash;
|
||||
using System.Linq;
|
||||
|
||||
|
||||
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>
|
||||
/// <param name="unleashĈlient"></param>
|
||||
public ApiController(IPackageManager pm, IUnleash unleashĈlient)
|
||||
{
|
||||
packageManager = pm;
|
||||
resources = packageManager.GetResources(unleashĈlient).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// API index
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet("~" + Constants.ApiVersion + "/index")]
|
||||
public IActionResult ApiIndex()
|
||||
{
|
||||
return Ok(new ApiIndexViewModel(packageManager.CatalogBaseUrl){ Version = PackageManager.BASE_API_LEVEL, Resources = resources });
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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.ApiVersion + 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));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using isnd.Services;
|
||||
using isnd.Entities;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Collections.Generic;
|
||||
using NuGet.Versioning;
|
||||
using isnd.Data.Packages.Catalog;
|
||||
using isnd.Data.Catalog;
|
||||
using isn.abst;
|
||||
|
||||
namespace isnd.Controllers
|
||||
{
|
||||
public partial class PackagesController
|
||||
{
|
||||
|
||||
// https://docs.microsoft.com/en-us/nuget/api/catalog-resource#versioning
|
||||
[HttpGet("~" + Constants.ApiVersion + ApiConfig.Catalog)]
|
||||
public async Task<IActionResult> CatalogIndex()
|
||||
{
|
||||
return Ok(await packageManager.GetCatalogIndexAsync());
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("~" + Constants.ApiVersion + ApiConfig.Registration + "/{id}/{version?}")]
|
||||
public async Task<IActionResult> CatalogRegistration(string id, string version)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(version))
|
||||
{
|
||||
var query = new Data.Catalog.RegistrationPageIndexQuery
|
||||
{
|
||||
Query = id,
|
||||
Prerelease = true
|
||||
};
|
||||
var index = await packageManager.GetPackageRegistrationIndexAsync(query);
|
||||
if (index == null) return NotFound();
|
||||
// query.TotalHits = result.Items.Select(i=>i.Items.Length).Aggregate((a,b)=>a+b);
|
||||
return Ok(index);
|
||||
}
|
||||
// return a Package
|
||||
var leaf = await packageManager.GetCatalogEntryAsync(id, version, null);
|
||||
|
||||
if (null == leaf) return NotFound(new { id, version });
|
||||
return Ok(leaf);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -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.ApiVersion + 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
{
|
||||
// Web get the paquet
|
||||
[HttpGet("~" + Constants.ApiVersion + ApiConfig.Package + "/{id}/{lower}/{idf}-{lowerf}."
|
||||
+ Constants.PaquetFileEstension)]
|
||||
public IActionResult GetPackage(
|
||||
[FromRoute][SafeName][Required] string id,
|
||||
[FromRoute][SafeName][Required] string lower,
|
||||
[FromRoute] string idf, [FromRoute] string lowerf)
|
||||
{
|
||||
var pkgpath = Path.Combine(isndSettings.PackagesRootDir,
|
||||
id, lower, $"{id}-{lower}." + Constants.PaquetFileEstension
|
||||
);
|
||||
|
||||
FileInfo pkgfi = new FileInfo(pkgpath);
|
||||
|
||||
if (!pkgfi.Exists)
|
||||
{
|
||||
return BadRequest("!pkgfi.Exists");
|
||||
}
|
||||
return File(pkgfi.OpenRead(), "application/zip; charset=binary");
|
||||
}
|
||||
|
||||
// Web get spec
|
||||
[HttpGet("~" + Constants.ApiVersion + Constants.SpecFileEstension + "/{id}/{lower}/{idf}-{lowerf}."
|
||||
+ Constants.SpecFileEstension)]
|
||||
public IActionResult GetNuspec(
|
||||
[FromRoute][SafeName][Required] string id,
|
||||
[FromRoute][SafeName][Required] string lower,
|
||||
[FromRoute][SafeName][Required] string idf,
|
||||
[FromRoute][SafeName][Required] string lowerf)
|
||||
{
|
||||
var pkgpath = Path.Combine(isndSettings.PackagesRootDir,
|
||||
id, lower, $"{id}." + Constants.SpecFileEstension);
|
||||
|
||||
FileInfo pkgfi = new FileInfo(pkgpath);
|
||||
if (!pkgfi.Exists)
|
||||
{
|
||||
return BadRequest("!pkgfi.Exists");
|
||||
}
|
||||
|
||||
return File(pkgfi.OpenRead(), "text/xml; charset=utf-8");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NuGet.Versioning;
|
||||
using isnd.Entities;
|
||||
using isn.abst;
|
||||
|
||||
namespace isnd.Controllers
|
||||
{
|
||||
public partial class PackagesController
|
||||
{
|
||||
|
||||
public IActionResult GetVersions(
|
||||
string id,
|
||||
string lower,
|
||||
bool prerelease = false,
|
||||
string packageType = null,
|
||||
int skip = 0,
|
||||
int take = 25)
|
||||
{
|
||||
if (take > maxTake)
|
||||
{
|
||||
ModelState.AddModelError("take", "Maximum exceeded");
|
||||
}
|
||||
// NugetVersion
|
||||
if (!NuGetVersion.TryParse(lower, out NuGetVersion parsedVersion))
|
||||
{
|
||||
ModelState.AddModelError("lower", "invalid version string");
|
||||
}
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
return Ok(new
|
||||
{
|
||||
versions = packageManager.GetVersions(
|
||||
id, parsedVersion, prerelease, packageType, skip, take)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,221 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NuGet.Packaging.Core;
|
||||
using NuGet.Versioning;
|
||||
using isnd.Data;
|
||||
using isnd.Helpers;
|
||||
using isnd.Entities;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using isn.abst;
|
||||
using isnd.Data.Packages;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
|
||||
namespace isnd.Controllers
|
||||
{
|
||||
|
||||
public partial class PackagesController
|
||||
{
|
||||
// TODO [Authorize(Policy = IsndConstants.RequireValidApiKey)]
|
||||
[HttpPut("~" + Constants.ApiVersion + 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 apikey = dbContext.ApiKeys.SingleOrDefault(k => k.Id == clearkey);
|
||||
if (apikey == null)
|
||||
{
|
||||
logger.LogError("403 : no api-key");
|
||||
return Unauthorized();
|
||||
}
|
||||
Commit commit = new Commit
|
||||
{
|
||||
Action = PackageAction.PublishPackage,
|
||||
TimeStamp = DateTime.Now
|
||||
};
|
||||
dbContext.Commits.Add(commit);
|
||||
|
||||
foreach (IFormFile file in Request.Form.Files)
|
||||
{
|
||||
string initpath = Path.Combine(Environment.GetEnvironmentVariable("TEMP") ??
|
||||
Environment.GetEnvironmentVariable("TMP") ?? "/tmp",
|
||||
$"isn-{Guid.NewGuid()}."+Constants.PaquetFileEstension);
|
||||
|
||||
using (FileStream fw = new FileStream(initpath, FileMode.Create))
|
||||
{
|
||||
file.CopyTo(fw);
|
||||
}
|
||||
|
||||
using (FileStream fw = new FileStream(initpath, FileMode.Open))
|
||||
{
|
||||
var archive = new ZipArchive(fw);
|
||||
|
||||
var spec = archive.Entries.FirstOrDefault(e => e.FullName.EndsWith("." + Constants.SpecFileEstension));
|
||||
if (spec == null) return BadRequest(new { error = "no " + Constants.SpecFileEstension + " from archive" });
|
||||
string pkgpath;
|
||||
NuGetVersion version;
|
||||
string pkgid;
|
||||
string fullpath;
|
||||
|
||||
using (var specstr = spec.Open())
|
||||
{
|
||||
NuspecCoreReader reader = new NuspecCoreReader(specstr);
|
||||
|
||||
string pkgdesc = reader.GetDescription();
|
||||
var types = reader.GetPackageTypes();
|
||||
pkgid = reader.GetId();
|
||||
version = reader.GetVersion();
|
||||
|
||||
string pkgidpath = Path.Combine(isndSettings.PackagesRootDir,
|
||||
pkgid);
|
||||
pkgpath = Path.Combine(pkgidpath, version.ToFullString());
|
||||
string name = $"{pkgid}-{version}."+Constants.PaquetFileEstension;
|
||||
fullpath = Path.Combine(pkgpath, name);
|
||||
|
||||
var destpkgiddir = new DirectoryInfo(pkgidpath);
|
||||
Package pkg = dbContext.Packages.SingleOrDefault(p => p.Id == pkgid);
|
||||
if (pkg != null)
|
||||
{
|
||||
if (pkg.OwnerId != apikey.UserId)
|
||||
{
|
||||
return new ForbidResult();
|
||||
}
|
||||
pkg.Description = pkgdesc;
|
||||
}
|
||||
else
|
||||
{
|
||||
pkg = new Package
|
||||
{
|
||||
Id = pkgid,
|
||||
Description = pkgdesc,
|
||||
OwnerId = apikey.UserId,
|
||||
LatestVersion = commit,
|
||||
};
|
||||
dbContext.Packages.Add(pkg);
|
||||
}
|
||||
if (!destpkgiddir.Exists) destpkgiddir.Create();
|
||||
|
||||
var source = new FileInfo(initpath);
|
||||
var dest = new FileInfo(fullpath);
|
||||
var destdir = new DirectoryInfo(dest.DirectoryName);
|
||||
if (dest.Exists)
|
||||
{
|
||||
// La version existe sur le disque,
|
||||
// mais si elle ne l'est pas en base de donnéés,
|
||||
// on remplace la version sur disque.
|
||||
var pkgv = dbContext.PackageVersions.Where(
|
||||
v => v.PackageId == pkg.Id
|
||||
);
|
||||
|
||||
if (pkgv !=null && pkgv.Count()==0)
|
||||
{
|
||||
dest.Delete();
|
||||
}
|
||||
else {
|
||||
logger.LogWarning("400 : pkgversion:existant");
|
||||
ModelState.AddModelError("pkgversion", "existant" );
|
||||
return BadRequest(ModelState);
|
||||
}
|
||||
}
|
||||
{
|
||||
if (!destdir.Exists) destdir.Create();
|
||||
source.MoveTo(fullpath);
|
||||
files.Add(name);
|
||||
string fullstringversion = version.ToFullString();
|
||||
var pkgvers = dbContext.PackageVersions.Where
|
||||
(v => v.PackageId == pkg.Id && v.FullString == fullstringversion);
|
||||
if (pkgvers.Count() > 0)
|
||||
{
|
||||
foreach (var v in pkgvers.ToArray())
|
||||
dbContext.PackageVersions.Remove(v);
|
||||
}
|
||||
|
||||
// FIXME default type or null
|
||||
if (types==null || types.Count==0)
|
||||
dbContext.PackageVersions.Add
|
||||
(new PackageVersion{
|
||||
|
||||
Package = pkg,
|
||||
Major = version.Major,
|
||||
Minor = version.Minor,
|
||||
Patch = version.Patch,
|
||||
Revision = version.Revision,
|
||||
IsPrerelease = version.IsPrerelease,
|
||||
FullString = version.ToFullString(),
|
||||
Type = null,
|
||||
LatestCommit = commit
|
||||
});
|
||||
else
|
||||
foreach (var type in types)
|
||||
{
|
||||
var pkgver = new PackageVersion
|
||||
{
|
||||
Package = pkg,
|
||||
Major = version.Major,
|
||||
Minor = version.Minor,
|
||||
Patch = version.Patch,
|
||||
IsPrerelease = version.IsPrerelease,
|
||||
FullString = version.ToFullString(),
|
||||
Type = type.Name,
|
||||
LatestCommit = commit
|
||||
};
|
||||
dbContext.PackageVersions.Add(pkgver);
|
||||
}
|
||||
|
||||
await dbContext.SaveChangesAsync();
|
||||
packageManager.ÛpdateCatalogForAsync(commit);
|
||||
|
||||
logger.LogInformation($"new paquet : {spec.Name}");
|
||||
}
|
||||
}
|
||||
using (var shacrypto = System.Security.Cryptography.SHA512.Create())
|
||||
{
|
||||
using (var stream = System.IO.File.OpenRead(fullpath))
|
||||
{
|
||||
var hash = shacrypto.ComputeHash(stream);
|
||||
var shafullname = fullpath + ".sha512";
|
||||
var hashtext = Convert.ToBase64String(hash);
|
||||
var hashtextbytes = Encoding.ASCII.GetBytes(hashtext);
|
||||
|
||||
using (var shafile = System.IO.File.OpenWrite(shafullname))
|
||||
{
|
||||
shafile.Write(hashtextbytes, 0, hashtextbytes.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
string nuspecfullpath = Path.Combine(pkgpath, pkgid + "." + Constants.SpecFileEstension);
|
||||
FileInfo nfpi = new FileInfo(nuspecfullpath);
|
||||
|
||||
if (nfpi.Exists)
|
||||
nfpi.Delete();
|
||||
spec.ExtractToFile(nuspecfullpath);
|
||||
}
|
||||
|
||||
}
|
||||
return Ok(ViewData);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex.Message);
|
||||
logger.LogError("Stack Trace: " + ex.StackTrace);
|
||||
return new ObjectResult(new { ViewData, ex.Message })
|
||||
{ StatusCode = 500 };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using isnd.Entities;
|
||||
using isn.abst;
|
||||
|
||||
namespace isnd.Controllers
|
||||
{
|
||||
|
||||
public partial class PackagesController
|
||||
{
|
||||
// GET {@id}?q={QUERY}&skip={SKIP}&take={TAKE}&prerelease={PRERELEASE}&semVerLevel={SEMVERLEVEL}&packageType={PACKAGETYPE}
|
||||
[HttpGet("~" + Constants.ApiVersion + ApiConfig.Search)]
|
||||
public IActionResult Search(
|
||||
string q,
|
||||
int skip = 0,
|
||||
int take = 25,
|
||||
bool prerelease = false,
|
||||
string semVerLevel = null,
|
||||
string packageType = null
|
||||
)
|
||||
{
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using isnd.Data;
|
||||
using isnd.Data.Catalog;
|
||||
using isnd.Helpers;
|
||||
using isnd.ViewModels;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace isnd.Controllers
|
||||
{
|
||||
|
||||
public partial class PackagesController
|
||||
{
|
||||
// Web search
|
||||
public async Task<IActionResult> Index(RegistrationPageIndexQuery model)
|
||||
{
|
||||
return View(new RegistrationPageIndexQueryAndResult{Query = model,
|
||||
Result = await packageManager.SearchPackageAsync(model)});
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Details(string pkgid)
|
||||
{
|
||||
if (pkgid == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var packageVersion = dbContext.PackageVersions
|
||||
.Include(p => p.Package)
|
||||
.Where(m => m.PackageId == pkgid)
|
||||
.OrderByDescending(p => p)
|
||||
;
|
||||
|
||||
if (packageVersion.Count() == 0)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
bool results = await packageVersion.AnyAsync();
|
||||
var latest = await packageVersion.FirstAsync();
|
||||
|
||||
return View("Details", new PackageDetailViewModel
|
||||
{
|
||||
ExternalUrl = isndSettings.ExternalUrl,
|
||||
latest = latest,
|
||||
pkgid = pkgid,
|
||||
totalHits = packageVersion.Count(),
|
||||
data = packageVersion.Take(MAX_PKG_VERSION_LIST).ToArray()
|
||||
});
|
||||
|
||||
}
|
||||
const int MAX_PKG_VERSION_LIST = 50;
|
||||
|
||||
[Authorize]
|
||||
public async Task<IActionResult> Delete(string pkgid, string version, string pkgtype)
|
||||
{
|
||||
if (pkgid == null || version == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
// var report = await packageManager.DeletePackageAsync(id, lower, type);
|
||||
|
||||
var packageVersion = await dbContext.PackageVersions.Include(p => p.Package)
|
||||
.FirstOrDefaultAsync(m => m.PackageId == pkgid
|
||||
&& m.FullString == version && m.Type == pkgtype);
|
||||
if (packageVersion == null) return NotFound();
|
||||
if (!User.IsOwner(packageVersion)) return Unauthorized();
|
||||
var pkg = await packageManager.GetPackageAsync(pkgid, version, pkgtype);
|
||||
return View(pkg);
|
||||
}
|
||||
|
||||
// POST: PackageVersion/Delete/5
|
||||
[HttpPost, ActionName("Delete")]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> DeleteConfirmed(string PackageId, string FullString,
|
||||
string Type)
|
||||
{
|
||||
PackageVersion packageVersion = await dbContext.PackageVersions
|
||||
.Include(pv => pv.Package)
|
||||
.FirstOrDefaultAsync(m => m.PackageId == PackageId
|
||||
&& m.FullString == FullString && m.Type == Type);
|
||||
if (packageVersion == null) return NotFound();
|
||||
if (!User.IsOwner(packageVersion)) return Unauthorized();
|
||||
|
||||
await packageManager.DeletePackageAsync(PackageId, FullString, Type);
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NuGet.Versioning;
|
||||
using isnd.Data;
|
||||
using isnd.Entities;
|
||||
using Unleash;
|
||||
using isnd.Services;
|
||||
using isnd.ViewModels;
|
||||
using System.Threading.Tasks;
|
||||
using isnd.Interfaces;
|
||||
using isn.Abstract;
|
||||
|
||||
namespace isnd.Controllers
|
||||
{
|
||||
[AllowAnonymous]
|
||||
public partial class PackagesController : Controller
|
||||
{
|
||||
const int maxTake = 100;
|
||||
private readonly Resource[] resources;
|
||||
private readonly ILogger<PackagesController> logger;
|
||||
private readonly IDataProtector protector;
|
||||
|
||||
private readonly IsndSettings isndSettings;
|
||||
readonly ApplicationDbContext dbContext;
|
||||
private readonly IPackageManager packageManager;
|
||||
private readonly IUnleash unleashĈlient;
|
||||
|
||||
public PackagesController(
|
||||
ILoggerFactory loggerFactory,
|
||||
IDataProtectionProvider provider,
|
||||
IOptions<IsndSettings> isndOptions,
|
||||
IUnleash unleashĈlient,
|
||||
ApplicationDbContext dbContext,
|
||||
IPackageManager pm)
|
||||
{
|
||||
logger = loggerFactory.CreateLogger<PackagesController>();
|
||||
isndSettings = isndOptions.Value;
|
||||
protector = provider.CreateProtector(isndSettings.ProtectionTitle);
|
||||
this.dbContext = dbContext;
|
||||
packageManager = pm;
|
||||
this.unleashĈlient = unleashĈlient;
|
||||
resources = packageManager.GetResources(unleashĈlient).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
@ -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; }
|
||||
}
|
||||
}
|
@ -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; }
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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; }
|
||||
}
|
||||
}
|
@ -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 DateTime CreationDate { get; set; }
|
||||
|
||||
public bool IsValid => CreationDate.AddDays(ValidityPeriodInDays) > DateTime.Now;
|
||||
}
|
||||
}
|
@ -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; }
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
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; }
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
namespace isnd.Data.ApiKeys
|
||||
{
|
||||
public class DeleteModel
|
||||
{
|
||||
public ApiKey ApiKey { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
namespace isnd.Data.ApiKeys
|
||||
{
|
||||
public class DetailModel : ApiKeyViewModel
|
||||
{
|
||||
public ApiKey ApiKey { get; set; }
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
namespace isnd.Data.ApiKeys
|
||||
{
|
||||
public class EditModel
|
||||
{
|
||||
public EditModel()
|
||||
{
|
||||
if (ApiKey==null) ApiKey = new ApiKey();
|
||||
}
|
||||
|
||||
public ApiKey ApiKey { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace isnd.Data.ApiKeys
|
||||
{
|
||||
public class IndexModel
|
||||
{
|
||||
public List<ApiKey> ApiKey { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
using NuGet.Versioning;
|
||||
|
||||
namespace isnd.Data.Catalog
|
||||
{
|
||||
public class AlternatePackage
|
||||
{
|
||||
public string id { get ; set; }
|
||||
public VersionRange range { get ; set; }
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
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;
|
||||
|
||||
namespace isnd.Data.Catalog
|
||||
{
|
||||
public class CatalogEntry : HappyIdOwner , IObject// , IPackageDetails
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a catalog entry
|
||||
/// </summary>
|
||||
/// <param name="id">package details url</param>
|
||||
/// <returns></returns>
|
||||
public CatalogEntry(string id): base(id)
|
||||
{
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// The ID of the package
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
|
||||
[JsonProperty("id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Package details url
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
|
||||
[JsonProperty("@id")]
|
||||
public string refid { get => GetId(); }
|
||||
|
||||
|
||||
[JsonProperty("@type")]
|
||||
public string[] RefType { get; protected set; } = new string[] { "PackageDetail" };
|
||||
|
||||
[JsonProperty("commitId")]
|
||||
public string CommitId { get; set; }
|
||||
|
||||
[JsonProperty("commitTimeStamp")]
|
||||
public DateTime CommitTimeStamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Authors
|
||||
/// </summary>
|
||||
/// <value>string or array of strings</value>
|
||||
public string authors { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The dependencies of the package, grouped by target framework
|
||||
/// </summary>
|
||||
/// <value>array of objects</value>
|
||||
public DependencyGroup[] dependencyGroups { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The deprecation associated with the package
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public Deprecation deprecation { get; set; }
|
||||
|
||||
[JsonProperty("description")]
|
||||
public string Description { get; set; }
|
||||
public string iconUrl { get; set; }
|
||||
public string language { get; set; }
|
||||
|
||||
|
||||
public string licenseUrl { get; set; }
|
||||
public string licenseExpression { get; set; }
|
||||
/// <summary>
|
||||
/// Should be considered as listed if absent
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public bool listed { get; set; }
|
||||
public string minClientVersion { get; set; }
|
||||
public string projectUrl { get; set; }
|
||||
|
||||
public bool requireLicenseAcceptance { get; set; }
|
||||
public string summary { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The tags
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public string tags { get; set; }
|
||||
public string title { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The security vulnerabilities of the package
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public Vulnerabilitie[] vulnerabilities { get; set; }
|
||||
|
||||
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 DateTime Published { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The full version string after normalization
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
[Required,JsonRequired]
|
||||
[JsonProperty("version")]
|
||||
public string Version { get; set; }
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
namespace isnd.Data.Catalog
|
||||
{
|
||||
public class DependencyGroup
|
||||
{
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace isnd.Data.Catalog
|
||||
{
|
||||
/// <summary>
|
||||
/// Hosts a catalog entry,
|
||||
/// the atomic content reference
|
||||
/// </summary>
|
||||
public class RegistrationLeaf
|
||||
{
|
||||
/*
|
||||
@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 CatalogEntry Entry { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The URL to the package content (.nupkg)
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
[JsonProperty("packageContent")]
|
||||
public string PackageContent { get; set; }
|
||||
}
|
||||
}
|
@ -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 : HappyIdOwner
|
||||
{
|
||||
[JsonProperty("@id")]
|
||||
public string Id { get => GetId(); }
|
||||
private readonly string pkgid;
|
||||
private readonly List<PackageVersion> items;
|
||||
|
||||
protected string Bid { get ; private set; }
|
||||
protected string ExternalUrl { get; }
|
||||
|
||||
public RegistrationPage (string bid, string pkgid, string extUrl) : base(bid + "/" + pkgid + "/index.json")
|
||||
{
|
||||
Bid = bid;
|
||||
Parent = Bid + $"/{pkgid}/index.json";
|
||||
ExternalUrl = extUrl;
|
||||
this.items = new List<PackageVersion>();
|
||||
this.pkgid = pkgid;
|
||||
}
|
||||
|
||||
public RegistrationPage(string bid, string pkgid, string extUrl, List<PackageVersion> versions) : this(bid, pkgid, extUrl)
|
||||
{
|
||||
AddVersionRange(versions);
|
||||
}
|
||||
|
||||
public string GetPackageId()
|
||||
{
|
||||
return pkgid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The array of registration leaves and their associate metadata
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
[JsonProperty("items")]
|
||||
|
||||
public CatalogEntry[] Items { get => items.Select((p) => p.ToLeave(Bid, ExternalUrl)).ToArray(); }
|
||||
|
||||
public void AddVersionRange(IEnumerable<PackageVersion> vitems)
|
||||
{
|
||||
if (vitems.Count() == 0) return;
|
||||
NuGetVersion upper = null;
|
||||
NuGetVersion lower = 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 (items.Contains(p)) 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) commitMax = p.CommitNId;
|
||||
items.Add(p);
|
||||
}
|
||||
Upper = upper.ToFullString();
|
||||
Lower = lower.ToFullString();
|
||||
CommitId = commitMax.ToString();
|
||||
}
|
||||
|
||||
/// <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; }
|
||||
public string CommitId { get; internal set; }
|
||||
public DateTime CommitTimeStamp { get; internal set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
using isnd.Data.Packages;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace isnd.Data.Catalog
|
||||
{
|
||||
public class RegistrationPageIndex : HappyIdOwner
|
||||
{
|
||||
|
||||
public RegistrationPageIndex(string url) : base(url)
|
||||
{
|
||||
Items = new List<RegistrationPage>();
|
||||
}
|
||||
|
||||
public RegistrationPageIndex(string bid, string id, string extUrl, IEnumerable<Package> pkgs) : base(bid + $"/{id}/index.json")
|
||||
{
|
||||
Items = new List<RegistrationPage>();
|
||||
long cnid = 0;
|
||||
var pkgsGroups = pkgs.GroupBy(l => l.Id);
|
||||
// Pour tous les groupes par Id
|
||||
foreach (var gsp in pkgsGroups)
|
||||
{
|
||||
var pkgsbi = gsp.ToArray();
|
||||
List<PackageVersion> versions = new List<PackageVersion>();
|
||||
|
||||
foreach(var l in pkgsbi.Select(p => p.Versions))
|
||||
{
|
||||
versions.AddRange(l);
|
||||
foreach (var pv in l)
|
||||
{
|
||||
if (pv.CommitNId> cnid)
|
||||
{
|
||||
cnid = pv.CommitNId;
|
||||
}
|
||||
}
|
||||
}
|
||||
Items.Add(new RegistrationPage(bid, gsp.Key, extUrl, versions));
|
||||
}
|
||||
CommitId = cnid.ToString();
|
||||
}
|
||||
|
||||
[JsonProperty("count")]
|
||||
public int Count { get => Items.Count; }
|
||||
|
||||
[JsonProperty("items")]
|
||||
public List<RegistrationPage> Items { get; set; }
|
||||
|
||||
public string CommitId { get; set; }
|
||||
public DateTime CommitTimeStamp { get; internal set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
using System.Collections.Generic;
|
||||
using isnd.Data.Packages;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace isnd.Data.Catalog
|
||||
{
|
||||
public class RegistrationPageIndexQuery
|
||||
{
|
||||
public RegistrationPageIndexQuery()
|
||||
{
|
||||
}
|
||||
|
||||
[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;
|
||||
}
|
||||
}
|
@ -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
|
||||
|
||||
}
|
||||
}
|
@ -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; }
|
||||
}
|
||||
}
|
@ -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")]
|
||||
DateTime CommitTimeStamp { get; }
|
||||
}
|
||||
}
|
@ -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 PackageRef : 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 DateTime CommitTimeStamp { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using isnd.Interfaces;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace isnd.Data.Packages.Catalog
|
||||
{
|
||||
public class Page : IObject
|
||||
{
|
||||
[JsonProperty("@id")]
|
||||
public string Id { get; set; }
|
||||
|
||||
[JsonProperty("parent")]
|
||||
public string Parent { get; set; }
|
||||
|
||||
[JsonProperty("items")]
|
||||
public virtual List<PackageRef> Items { get; set; }
|
||||
public string CommitId { get; set; }
|
||||
public DateTime CommitTimeStamp { get; set; }
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue