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/
|
/packages/
|
||||||
/bin/
|
bower_components/
|
||||||
/obj/
|
/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
|
````sh
|
||||||
|
|
||||||
nuget sources remove -Name dev
|
dotnet user-secrets set ConnectionStrings:DefaultConnection "Server=<lame-pgserver>;Port=<lame-pgport>;Database=<lame-dbname>;Username=<lame-dbusername>;Password=<lame-dbpass>;"
|
||||||
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
|
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