refabrique : une librairie serveur

vnext
Paul Schneider 7 years ago
parent 1179767112
commit 9019f39ede
25 changed files with 457 additions and 86 deletions

@ -23,13 +23,11 @@
"Chat"
]
},
"projectUrl": "",
"licenseUrl": "",
"tooling": {
"defaultNamespace": "Yavsc"
},
"dependencies": {
"Newtonsoft.Json": "10.0.2"
"Newtonsoft.Json": "9.0.1"
},
"frameworks": {
"net451": {

@ -3,7 +3,7 @@
"version": 2,
"targets": {
".NETFramework,Version=v4.5.1": {
"Newtonsoft.Json/10.0.2": {
"Newtonsoft.Json/9.0.1": {
"type": "package",
"compile": {
"lib/net45/Newtonsoft.Json.dll": {}
@ -14,7 +14,7 @@
}
},
".NETFramework,Version=v4.5.1/debian.9-x86": {
"Newtonsoft.Json/10.0.2": {
"Newtonsoft.Json/9.0.1": {
"type": "package",
"compile": {
"lib/net45/Newtonsoft.Json.dll": {}
@ -25,7 +25,7 @@
}
},
".NETFramework,Version=v4.5.1/debian.9-x64": {
"Newtonsoft.Json/10.0.2": {
"Newtonsoft.Json/9.0.1": {
"type": "package",
"compile": {
"lib/net45/Newtonsoft.Json.dll": {}
@ -37,9 +37,9 @@
}
},
"libraries": {
"Newtonsoft.Json/10.0.2": {
"Newtonsoft.Json/9.0.1": {
"type": "package",
"sha512": "iwElSU2IXmwGvytJsezyDML2ZWDkG2JzTYzlU/BNlmzMdlmRvbnwITsGGY74gwVEpDli1UdOLkMT7/3jxWvXzA==",
"sha512": "U82mHQSKaIk+lpSVCbWYKNavmNH1i5xrExDEquU1i6I5pV6UMOqRnJRSlKO3cMPfcpp0RgDY+8jUXHdQ4IfXvw==",
"files": [
"lib/net20/Newtonsoft.Json.dll",
"lib/net20/Newtonsoft.Json.xml",
@ -51,15 +51,12 @@
"lib/net45/Newtonsoft.Json.xml",
"lib/netstandard1.0/Newtonsoft.Json.dll",
"lib/netstandard1.0/Newtonsoft.Json.xml",
"lib/netstandard1.3/Newtonsoft.Json.dll",
"lib/netstandard1.3/Newtonsoft.Json.xml",
"lib/portable-net40+sl5+win8+wpa81+wp8/Newtonsoft.Json.dll",
"lib/portable-net40+sl5+win8+wpa81+wp8/Newtonsoft.Json.xml",
"lib/portable-net45+win8+wpa81+wp8/Newtonsoft.Json.dll",
"lib/portable-net45+win8+wpa81+wp8/Newtonsoft.Json.xml",
"LICENSE.md",
"Newtonsoft.Json.10.0.2.nupkg",
"Newtonsoft.Json.10.0.2.nupkg.sha512",
"lib/portable-net40+sl5+wp80+win8+wpa81/Newtonsoft.Json.dll",
"lib/portable-net40+sl5+wp80+win8+wpa81/Newtonsoft.Json.xml",
"lib/portable-net45+wp80+win8+wpa81/Newtonsoft.Json.dll",
"lib/portable-net45+wp80+win8+wpa81/Newtonsoft.Json.xml",
"Newtonsoft.Json.9.0.1.nupkg",
"Newtonsoft.Json.9.0.1.nupkg.sha512",
"Newtonsoft.Json.nuspec",
"tools/install.ps1"
]
@ -67,7 +64,7 @@
},
"projectFileDependencyGroups": {
"": [
"Newtonsoft.Json >= 10.0.2"
"Newtonsoft.Json >= 9.0.1"
],
".NETFramework,Version=v4.5.1": [
"fx/System.ComponentModel.DataAnnotations >= 4.0.0",

@ -0,0 +1,137 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Yavsc.Server.Model;
namespace Yavsc.Server.Helpers
{
/// <summary>
/// Thanks to Stefan @ Stackoverflow
/// </summary>
public class RequestHelper
{
string WRPostMultipart(string url, Dictionary<string, object> parameters, string authorizationHeader = null)
{
string boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
byte[] boundaryBytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n");
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.ContentType = "multipart/form-data; boundary=" + boundary;
request.Method = "POST";
request.KeepAlive = true;
request.Credentials = System.Net.CredentialCache.DefaultCredentials;
if (authorizationHeader != null)
request.Headers["Authorization"] = authorizationHeader;
if (parameters != null && parameters.Count > 0)
{
using (Stream requestStream = request.GetRequestStream())
{
using (WebResponse response = request.GetResponse())
{
foreach (KeyValuePair<string, object> pair in parameters)
{
requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
if (pair.Value is FormFile)
{
FormFile file = pair.Value as FormFile;
string header = "Content-Disposition: form-data; name=\"" + pair.Key + "\"; filename=\"" + file.Name + "\"\r\nContent-Type: " + file.ContentType + "\r\n\r\n";
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(header);
requestStream.Write(bytes, 0, bytes.Length);
byte[] buffer = new byte[32768];
int bytesRead;
if (file.Stream == null)
{
// upload from file
using (FileStream fileStream = File.OpenRead(file.FilePath))
{
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
requestStream.Write(buffer, 0, bytesRead);
fileStream.Close();
}
}
else
{
// upload from given stream
while ((bytesRead = file.Stream.Read(buffer, 0, buffer.Length)) != 0)
requestStream.Write(buffer, 0, bytesRead);
}
}
else
{
string data = "Content-Disposition: form-data; name=\"" + pair.Key + "\"\r\n\r\n" + pair.Value;
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(data);
requestStream.Write(bytes, 0, bytes.Length);
}
}
byte[] trailer = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n");
requestStream.Write(trailer, 0, trailer.Length);
requestStream.Close();
using (Stream responseStream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(responseStream))
{
return reader.ReadToEnd();
}
} // end WebResponse response
} // end using requestStream
}
else throw new ArgumentOutOfRangeException("no parameter found ");
}
public static async Task<string> PostMultipart(string url, FormFile[] formFiles, string access_token = null)
{
if (formFiles != null && formFiles.Length > 0)
{
var client = new HttpClient();
var formData = new MultipartFormDataContent();
if (access_token != null)
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", access_token);
foreach (var formFile in formFiles)
{
HttpContent fileStreamContent = new StreamContent(formFile.Stream);
if (formFile.ContentType!=null)
fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue(formFile.ContentType);
else fileStreamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
// fileStreamContent.Headers.ContentDisposition = formFile.ContentDisposition!=null? new ContentDispositionHeaderValue(
// formFile.ContentDisposition) : new ContentDispositionHeaderValue("form-data; name=\"file\"; filename=\"" + formFile.Name + "\"");
fileStreamContent.Headers.Add("Content-Disposition", formFile.ContentDisposition);
fileStreamContent.Headers.Add("Content-Length", formFile.Stream.Length.ToString());
//fileStreamContent.Headers.Add("FilePath", formFile.FilePath);
formData.Add(fileStreamContent, "file", formFile.Name);
}
var response = client.PostAsync(url, formData).Result;
if (!response.IsSuccessStatusCode)
{
return null;
}
return await response.Content.ReadAsStringAsync();
} // end if formFiles != null
return null;
}
}
}

@ -25,7 +25,7 @@ using Newtonsoft.Json;
using System;
using System.Json;
namespace Yavsc.Helpers
namespace Yavsc.Server.Helpers
{
/// <summary>
/// Simple json post method.

@ -0,0 +1,26 @@
using System.IO;
using System.Net.Mime;
namespace Yavsc.Server.Model
{
public class FormFile
{
public string Name { get; set; }
string contentDispositionString;
public string ContentDisposition { get {
return contentDispositionString;
} set {
ContentDisposition contentDisposition = new ContentDisposition(value);
Name = contentDisposition.FileName;
contentDispositionString = value;
} }
public string ContentType { get; set; }
public string FilePath { get; set; }
public Stream Stream { get; set; }
}
}

@ -0,0 +1,48 @@
{
"version": "1.0.0-*",
"description": "Yavsc server common library",
"authors": [
"Paul Schneider <paul@pschneider.fr>"
],
"packOptions": {
"repository": {
"type": "git",
"url": "https://github.com/pazof/yavsc"
},
"licenseUrl": "https://github.com/pazof/yavsc/blob/vnext/LICENSE",
"requireLicenseAcceptance": true,
"owners": [
"Paul Schneider <paul@pschneider.fr>"
],
"summary": "Yet another very small company",
"projectUrl": "http://yavsc.pschneider.fr",
"tags": [
"Blog",
"Blog",
"PoS",
"Chat"
]
},
"tooling": {
"defaultNamespace": "Yavsc"
},
"dependencies": {
"Newtonsoft.Json": "9.0.1",
"Yavsc.Abstract": {
"target": "project",
"type": "build"
}
},
"frameworks": {
"net451": {
"frameworkAssemblies": {
"System.ComponentModel.DataAnnotations": "4.0.0",
"System.Json": "4.0.0",
"System.Net": "4.0.0.0",
"System.Net.Http": "4.0.0.0",
"System.Xml": "4.0.0.0",
"System": "4.0.0.0"
}
}
}
}

@ -0,0 +1,115 @@
{
"locked": false,
"version": 2,
"targets": {
".NETFramework,Version=v4.5.1": {
"Newtonsoft.Json/9.0.1": {
"type": "package",
"compile": {
"lib/net45/Newtonsoft.Json.dll": {}
},
"runtime": {
"lib/net45/Newtonsoft.Json.dll": {}
}
},
"Yavsc.Abstract/1.0.5": {
"type": "project",
"framework": ".NETFramework,Version=v4.5.1",
"dependencies": {
"Newtonsoft.Json": "9.0.1"
},
"frameworkAssemblies": [
"System.ComponentModel.DataAnnotations",
"System.Json"
]
}
},
".NETFramework,Version=v4.5.1/debian.9-x86": {
"Newtonsoft.Json/9.0.1": {
"type": "package",
"compile": {
"lib/net45/Newtonsoft.Json.dll": {}
},
"runtime": {
"lib/net45/Newtonsoft.Json.dll": {}
}
},
"Yavsc.Abstract/1.0.5": {
"type": "project",
"framework": ".NETFramework,Version=v4.5.1",
"dependencies": {
"Newtonsoft.Json": "9.0.1"
},
"frameworkAssemblies": [
"System.ComponentModel.DataAnnotations",
"System.Json"
]
}
},
".NETFramework,Version=v4.5.1/debian.9-x64": {
"Newtonsoft.Json/9.0.1": {
"type": "package",
"compile": {
"lib/net45/Newtonsoft.Json.dll": {}
},
"runtime": {
"lib/net45/Newtonsoft.Json.dll": {}
}
},
"Yavsc.Abstract/1.0.5": {
"type": "project",
"framework": ".NETFramework,Version=v4.5.1",
"dependencies": {
"Newtonsoft.Json": "9.0.1"
},
"frameworkAssemblies": [
"System.ComponentModel.DataAnnotations",
"System.Json"
]
}
}
},
"libraries": {
"Yavsc.Abstract/1.0.5": {
"type": "project",
"path": "../Yavsc.Abstract/project.json"
},
"Newtonsoft.Json/9.0.1": {
"type": "package",
"sha512": "U82mHQSKaIk+lpSVCbWYKNavmNH1i5xrExDEquU1i6I5pV6UMOqRnJRSlKO3cMPfcpp0RgDY+8jUXHdQ4IfXvw==",
"files": [
"lib/net20/Newtonsoft.Json.dll",
"lib/net20/Newtonsoft.Json.xml",
"lib/net35/Newtonsoft.Json.dll",
"lib/net35/Newtonsoft.Json.xml",
"lib/net40/Newtonsoft.Json.dll",
"lib/net40/Newtonsoft.Json.xml",
"lib/net45/Newtonsoft.Json.dll",
"lib/net45/Newtonsoft.Json.xml",
"lib/netstandard1.0/Newtonsoft.Json.dll",
"lib/netstandard1.0/Newtonsoft.Json.xml",
"lib/portable-net40+sl5+wp80+win8+wpa81/Newtonsoft.Json.dll",
"lib/portable-net40+sl5+wp80+win8+wpa81/Newtonsoft.Json.xml",
"lib/portable-net45+wp80+win8+wpa81/Newtonsoft.Json.dll",
"lib/portable-net45+wp80+win8+wpa81/Newtonsoft.Json.xml",
"Newtonsoft.Json.9.0.1.nupkg",
"Newtonsoft.Json.9.0.1.nupkg.sha512",
"Newtonsoft.Json.nuspec",
"tools/install.ps1"
]
}
},
"projectFileDependencyGroups": {
"": [
"Newtonsoft.Json >= 9.0.1",
"Yavsc.Abstract "
],
".NETFramework,Version=v4.5.1": [
"fx/System.ComponentModel.DataAnnotations >= 4.0.0",
"fx/System.Json >= 4.0.0",
"fx/System.Net >= 4.0.0",
"fx/System.Xml >= 4.0.0",
"fx/System >= 4.0.0"
]
}
}

@ -52,18 +52,18 @@ namespace Yavsc.ApiControllers
return Ok(files);
}
[HttpPost]
public IActionResult Post(string subdir="", string names = null)
[HttpPost("{subdir}")]
public IActionResult Post(string subdir="")
{
string root = null;
string [] destinationFileNames = names?.Split('/');
string destDir = null;
List<FileRecievedInfo> received = new List<FileRecievedInfo>();
InvalidPathException pathex = null;
try {
root = User.InitPostToFileSystem(subdir);
destDir = User.InitPostToFileSystem(subdir);
} catch (InvalidPathException ex) {
pathex = ex;
}
logger.LogInformation($"Recieving files, saved in '{destDir}' (specified ad '{subdir}').");
if (pathex!=null)
return new BadRequestObjectResult(pathex);
var uid = User.GetUserId();
@ -72,11 +72,11 @@ namespace Yavsc.ApiControllers
);
int i=0;
logger.LogInformation($"Recieving {Request.Form.Files.Count} files.");
foreach (var f in Request.Form.Files)
{
var destFileName = destinationFileNames?.Length >i ? destinationFileNames[i] : null;
var item = user.ReceiveUserFile(root, f, destFileName);
var item = user.ReceiveUserFile(destDir, f);
dbContext.SaveChanges(User.GetUserId());
received.Add(item);
logger.LogInformation($"Recieved '{item.FileName}'.");

@ -46,7 +46,7 @@ namespace Yavsc.Helpers
}
var di = new DirectoryInfo(root);
if (!di.Exists) di.Create();
return root;
return di.FullName;
}
public static void DeleteUserFile(this ApplicationUser user, string fileName)
@ -66,6 +66,7 @@ namespace Yavsc.Helpers
ContentDisposition contentDisposition = new ContentDisposition(f.ContentDisposition);
item.FileName = Yavsc.Abstract.FileSystem.FileSystemHelpers.FilterFileName (destFileName ?? contentDisposition.FileName);
item.MimeType = contentDisposition.DispositionType;
item.DestDir = root;
var fi = new FileInfo(Path.Combine(root, item.FileName));
if (fi.Exists)
{

@ -39,6 +39,8 @@ namespace Yavsc.Helpers
using Models;
using Models.Calendar;
using Services;
using Server.Helpers;
/// <summary>
/// Google helpers.

@ -31,16 +31,17 @@ using Google.Apis.Calendar.v3.Data;
using System.Collections.Generic;
using System.Linq;
using Google.Apis.Services;
using System.Threading;
using Google.Apis.Auth.OAuth2.Flows;
using Google.Apis.Auth.OAuth2.Responses;
using Google.Apis.Util;
namespace Yavsc.Services
{
using System.Threading;
using Google.Apis.Auth.OAuth2.Flows;
using Google.Apis.Auth.OAuth2.Responses;
using Google.Apis.Util;
using Yavsc.Helpers;
using Yavsc.Models;
using Yavsc.Models.Calendar;
using Yavsc.Server.Helpers;
using Yavsc.ViewModels.Calendar;
/// <summary>

@ -19,8 +19,8 @@
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
using System.Threading.Tasks;
using Yavsc.Helpers;
using Yavsc.Models.Google;
using Yavsc.Server.Helpers;
namespace Yavsc.GoogleApis
{

@ -5,7 +5,6 @@ using MimeKit;
using MailKit.Security;
using System;
using Yavsc.Models.Messaging;
using Yavsc.Helpers;
using Microsoft.AspNet.Identity;
using Yavsc.Models;
using Yavsc.Models.Google.Messaging;
@ -15,6 +14,7 @@ using Yavsc.Interfaces.Workflow;
using System.Linq;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Yavsc.Server.Helpers;
namespace Yavsc.Services
{

@ -122,6 +122,11 @@
"Microsoft.AspNet.OWin": "1.0.0-rc1-final",
"System.Json": "4.0.20126.16343",
"Yavsc.Abstract": {
"target": "project",
"type": "build"
},
"Yavsc.Server": {
"target": "project",
"type": "build"
},
"Microsoft.AspNet.Http.Extensions": "1.0.0-rc1-final",
@ -163,4 +168,4 @@
"prepublish": "gulp min"
},
"embed": "Views/**/*.cshtml"
}
}

@ -1,9 +0,0 @@
using System;
class YaDaemon
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}

@ -1,8 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.0</TargetFramework>
</PropertyGroup>
</Project>

@ -1,5 +0,0 @@
{
"sdk": {
"version": "1.0.0-rc4-004771"
}
}

@ -2,7 +2,7 @@
"projects": [
"Yavsc",
"Yavsc.Abstract",
"YaTask",
"Yavsc.Server",
"testOauthClient"
],
"sdk": {

@ -1,18 +1,24 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Json;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
using Microsoft.Extensions.Logging;
using Yavsc.Helpers;
using Yavsc.Server.Helpers;
using Yavsc.Server.Model;
namespace testOauthClient.Controllers
{
public class HomeController : Controller
{
ILogger _logger;
public HomeController(ILoggerFactory loggerFactory)
{
_logger=loggerFactory.CreateLogger<HomeController>();
@ -39,6 +45,45 @@ namespace testOauthClient.Controllers
}
[HttpPost]
public async Task<IActionResult> PostFiles(string subdir)
{
string results = null;
_logger.LogInformation($"{Request.Form.Files.Count} file(s) to send");
// TODO better uri construction in production environment
List<FormFile> args = new List<FormFile>();
foreach (var formFile in Request.Form.Files)
{
_logger.LogWarning($"Treating {formFile.ContentDisposition}");
var memStream = new MemoryStream();
const int sz = 1024*64;
byte [] buffer = new byte[sz];
using (var innerStream = formFile.OpenReadStream()) {
int szRead = 0;
do {
szRead = innerStream.Read(buffer,0,sz);
memStream.Write(buffer,0,szRead);
} while (szRead>0);
}
memStream.Seek(0,SeekOrigin.Begin);
args.Add(
new FormFile {
ContentDisposition = formFile.ContentDisposition,
ContentType = formFile.ContentType,
Stream = memStream
});
}
string uri = "http://dev.pschneider.fr/api/fs/"+System.Uri.EscapeDataString(subdir);
_logger.LogInformation($"Posting data to '{uri}'...");
results = await RequestHelper.PostMultipart(uri, args.ToArray(), AccessToken);
_logger.LogInformation("Data posted.");
return View("Index", model: results);
}
[HttpPost]
public async Task<IActionResult> PostDeviceInfo(CancellationToken cancellationToken)
{

@ -112,12 +112,15 @@ namespace testOauthClient
string email = null;
if (emails !=null)
email = emails.First?.Value<string>();
context.Identity.AddClaim(
new Claim( ClaimTypes.NameIdentifier,identifier));
context.Identity.AddClaim(
new Claim( ClaimTypes.Name,givenName));
context.Identity.AddClaim(
new Claim( ClaimTypes.Email,email));
if (identifier!=null)
context.Identity.AddClaim(
new Claim( ClaimTypes.NameIdentifier,identifier));
if (givenName!=null)
context.Identity.AddClaim(
new Claim( ClaimTypes.Name,givenName));
if (email!=null)
context.Identity.AddClaim(
new Claim( ClaimTypes.Email,email));
// TODO add all emails and roles
}

@ -5,13 +5,12 @@
<h3>@ViewData["Message"]</h3>
<address>
One Microsoft Way<br />
Redmond, WA 98052-6399<br />
<abbr title="Phone">P:</abbr>
425.555.0100
Paul Schneider<br />
<abbr title="Adresse postale">A:</abbr> Boulevard Aristide Briand - 92150 Suresnes
<abbr title="Mobile">M:</abbr> 336 51 14 15 64
</address>
<address>
<strong>Support:</strong> <a href="mailto:Support@example.com">Support@example.com</a><br />
<strong>Marketing:</strong> <a href="mailto:Marketing@example.com">Marketing@example.com</a>
<strong>Support:</strong> <a href="mailto:contact@pschneider.fr">contact@pschneider.fr</a><br />
<strong>Marketing:</strong> <a href="mailto:paul@pschneider.fr">paul@pschneider.fr</a>
</address>

@ -12,7 +12,7 @@
}
</p>
if (!string.IsNullOrEmpty(Model)) {
if (Model!=null) {
<h3>Message received from the resource controller: @Model</h3>
}
@ -22,6 +22,10 @@
<form action="~/Home/PostDeviceInfo" method="post">
<button class="btn btn-lg btn-warning" type="submit">Post device info</button>
</form>
<form action="~/Home/PostFiles/?subdir=test" method="post" enctype="multipart/form-data">
Envoyer vers le dossier &quot;test&quot; <input type="file" name="file" multiple/>
<button class="btn btn-lg btn-warning" type="submit">Post files</button>
</form>
<a class="btn btn-lg btn-danger" href="/signout">Sign out</a>
}

@ -1,12 +1,12 @@
{
"licence": "GNU GPL",
"name": "testOauthClient",
"version": "0.0.0",
"devDependencies": {
"gulp": "^3.9.0",
"gulp-concat": "2.5.2",
"gulp-cssmin": "0.1.7",
"gulp-uglify": "1.2.0",
"rimraf": "2.2.8"
}
}
"licence": "GNU GPL v3",
"name": "test-oauth-client",
"version": "0.0.0",
"devDependencies": {
"gulp": "^3.9.1",
"gulp-concat": "^2.6.1",
"gulp-cssmin": "^0.2.0",
"gulp-uglify": "^3.0.0",
"rimraf": "^2.6.2"
}
}

@ -8,6 +8,11 @@
},
"dependencies": {
"Yavsc.Abstract": {
"target": "project",
"type": "build"
},
"Yavsc.Server": {
"target": "project",
"type": "build"
},
"Microsoft.AspNet.Authentication.Cookies": "1.0.0-rc1-final",
@ -32,9 +37,10 @@
"frameworks": {
"dnx451": {
"dependencies": {
"System.Json": "4.0.0"
"System.Json": "4.0.20126.16343"
}
}
},
"net451": {}
},
"exclude": [
"wwwroot",
@ -56,4 +62,4 @@
"gulp min"
]
}
}
}

@ -8,6 +8,12 @@
},
{
"path": "cli"
},
{
"path": "testOauthClient"
},
{
"path": "Yavsc.Server"
}
],
"settings": {

Loading…