try rewirte of all backoffice controllers to ApiControllers

This commit is contained in:
2020-10-27 15:47:23 +01:00
parent edfb6ec2e5
commit 6eb81a5eeb
37 changed files with 315 additions and 354 deletions
+9 -3
View File
@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using Raven.Client.Documents;
using Raven.Client.Documents.Session;
using System;
@@ -29,15 +30,18 @@ namespace zero.Core.Api
protected UserManager<User> UserManager { get; private set; }
protected ILogger<ApplicationContext> Logger { get; private set; }
static IList<IApplication> Apps { get; set; }
public ApplicationContext(IDocumentStore raven, IZeroOptions options, UserManager<User> userManager)
public ApplicationContext(IDocumentStore raven, IZeroOptions options, UserManager<User> userManager, ILogger<ApplicationContext> logger)
{
Raven = raven;
Options = options;
UserManager = userManager;
Logger = logger;
}
@@ -62,7 +66,9 @@ namespace zero.Core.Api
if (app == null)
{
throw new NullReferenceException($"Could not resolve application for host {context.Request.Host}");
Logger.LogWarning($"Could not resolve application for host $host", context.Request.Host);
IList<IApplication> apps = await GetApplications();
app = apps.FirstOrDefault();
}
App = app;
@@ -171,7 +177,7 @@ namespace zero.Core.Api
using IAsyncDocumentSession session = Raven.OpenAsyncSession();
IApplication app = await session.LoadAsync<Application>(appId);
return new ApplicationContext(Raven, Options, UserManager)
return new ApplicationContext(Raven, Options, UserManager, Logger)
{
App = app,
AppId = appId
@@ -7,19 +7,17 @@ namespace zero.Core.Options
{
public class BackofficeJsonSerlializerSettings : JsonSerializerSettings
{
public BackofficeJsonSerlializerSettings() : this(false) { }
public BackofficeJsonSerlializerSettings(bool typed)
public BackofficeJsonSerlializerSettings()
{
Setup(this, typed);
Setup(this);
}
public static void Setup(JsonSerializerSettings settings, bool typed = false)
public static void Setup(JsonSerializerSettings settings)
{
settings.Converters.Add(new StringEnumConverter(new CamelCaseNamingStrategy()));
settings.Converters.Add(new RefJsonConverter());
settings.ContractResolver = new ZeroJsonContractResolver();
//settings.Converters.Add(new RefJsonConverter());
//settings.ContractResolver = new ZeroJsonContractResolver();
settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
settings.TypeNameHandling = TypeNameHandling.Objects;
//settings.SerializationBinder = new ZeroInterfaceBinder();
+1 -1
View File
@@ -25,7 +25,7 @@ namespace zero.Core.Routing
AffectedTypes = new Type[1] { typeof(T) };
DefaultEndpoint = new RouteProviderEndpoint()
{
Controller = "DefaultRoute",
Controller = "ZeroFrontend",
Action = "Index"
};
}
@@ -9,7 +9,7 @@
{
DefaultEndpoint = new RouteProviderEndpoint()
{
Controller = "DefaultRoute",
Controller = "ZeroFrontend",
Action = "Index"
};
}
+2 -2
View File
@@ -20,8 +20,8 @@
<PackageReference Include="FluentValidation" Version="9.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0-preview.8.20407.11" />
<PackageReference Include="RavenDB.Client" Version="5.0.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0-rc.2.20475.5" />
<PackageReference Include="RavenDB.Client" Version="5.0.3" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.1" />
</ItemGroup>
+8 -2
View File
@@ -17,9 +17,15 @@ export default {
},
// get all applications
getAll(query)
getAll()
{
return Axios.get(base + 'getAll', { params: query }).then(res => Promise.resolve(res.data));
return Axios.get(base + 'getAll').then(res => Promise.resolve(res.data));
},
// get applications by query
getByQuery(query)
{
return Axios.get(base + 'getByQuery', { params: query }).then(res => Promise.resolve(res.data));
},
// get all application features
+14 -36
View File
@@ -1,9 +1,11 @@
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
using zero.Core.Api;
using zero.Core.Entities;
using zero.Core.Extensions;
using zero.Core.Identity;
using zero.Web.Models;
namespace zero.Web.Controllers
{
@@ -11,58 +13,34 @@ namespace zero.Web.Controllers
public class ApplicationsController : BackofficeController
{
IApplicationsApi Api;
IApplication Blueprint;
public ApplicationsController(IApplicationsApi api, IApplication blueprint)
public ApplicationsController(IApplicationsApi api)
{
Api = api;
Blueprint = blueprint;
}
/// <summary>
/// Get translation by id
/// </summary>
public IActionResult GetEmpty() => Edit(Blueprint.Clone());
public EditModel<IApplication> GetEmpty([FromServices] IApplication blueprint) => Edit(blueprint);
/// <summary>
/// Get translation by id
/// </summary>
public async Task<IActionResult> GetById([FromQuery] string id) => Edit(await Api.GetById(id));
public async Task<EditModel<IApplication>> GetById([FromQuery] string id) => Edit(await Api.GetById(id));
/// <summary>
/// Get all translations
/// </summary>
public async Task<IActionResult> GetAll([FromQuery] ListQuery<IApplication> query = null)
{
if (query == null)
{
return Json(await Api.GetAll());
}
return Json(await Api.GetByQuery(query));
}
public async Task<IList<IApplication>> GetAll() => await Api.GetAll();
/// <summary>
/// Get all available features to select
/// </summary>
public IActionResult GetAllFeatures() => Json(Options.Features.GetAllItems());
public async Task<ListResult<IApplication>> GetByQuery([FromQuery] ListQuery<IApplication> query) => await Api.GetByQuery(query);
/// <summary>
/// Save translation
/// </summary>
public IReadOnlyCollection<IFeature> GetAllFeatures() => Options.Features.GetAllItems();
[HttpPost]
[ZeroAuthorize(Permissions.Settings.Applications, PermissionsValue.Update)]
public async Task<IActionResult> Save([FromBody] IApplication model) => Json(await Api.Save(model));
public async Task<EntityResult<IApplication>> Save([FromBody] IApplication model) => await Api.Save(model);
/// <summary>
/// Deletes a translation
/// </summary>
[HttpDelete]
[ZeroAuthorize(Permissions.Settings.Applications, PermissionsValue.Update)]
public async Task<IActionResult> Delete([FromQuery] string id) => Json(await Api.Delete(id));
public async Task<EntityResult<IApplication>> Delete([FromQuery] string id) => await Api.Delete(id);
}
}
@@ -21,59 +21,29 @@ namespace zero.Web.Controllers
}
/// <summary>
/// Returns the current user
/// </summary>
public async Task<IActionResult> GetUser()
{
return Edit(await Api.GetUser());
}
public async Task<EditModel<User>> GetUser() => Edit(await Api.GetUser());
public EntityResult IsLoggedIn() => EntityResult.Maybe(Api.IsLoggedIn());
/// <summary>
/// If a user is logged in
/// </summary>
public IActionResult IsLoggedIn()
{
return Json(EntityResult.Maybe(Api.IsLoggedIn()));
}
/// <summary>
/// Tries a login for a user with username/password
/// </summary>
[HttpPost]
public async Task<IActionResult> LoginUser([FromBody] LoginModel model)
{
EntityResult result = await Api.Login(model.Email, model.Password, model.IsPersistent);
return Json(result);
}
public async Task<EntityResult> LoginUser([FromBody] LoginModel model) => await Api.Login(model.Email, model.Password, model.IsPersistent);
/// <summary>
/// Logout for the current user
/// </summary>
[HttpPost, ZeroAuthorize]
public async Task<IActionResult> LogoutUser()
public async Task<EntityResult> LogoutUser()
{
await Api.Logout();
return Json(EntityResult.Success());
return EntityResult.Success();
}
/// <summary>
/// Try to switch selected application for user
/// </summary>
[HttpPost, ZeroAuthorize]
public async Task<IActionResult> SwitchApp(string appId)
public async Task<EntityResult> SwitchApp(string appId)
{
User user = await Api.GetUser();
bool isSuccess = await AppContext.TrySwitchForUser(user, appId);
return Json(new EntityResult()
{
IsSuccess = isSuccess
});
return EntityResult.Maybe(await AppContext.TrySwitchForUser(user, appId));
}
}
}
+12 -16
View File
@@ -16,7 +16,9 @@ namespace zero.Web.Controllers
{
[ZeroAuthorize]
[ServiceFilter(typeof(ModelStateValidationFilterAttribute))]
public abstract class BackofficeController : Controller
[ApiController]
[Route("getsreplaced/[controller]/[action]")]
public abstract class BackofficeController : ControllerBase
{
IZeroOptions _options;
IToken _token;
@@ -28,16 +30,10 @@ namespace zero.Web.Controllers
static Type AppAwareShareableType = typeof(IAppAwareShareableEntity);
/// <summary>
/// Creates a Microsoft.AspNetCore.Mvc.JsonResult object that serializes the specified data object to JSON.
/// </summary>
public JsonResult Json(object data, bool typed) => Json(data);
/// <summary>
/// Creates an edit model with appropriate options and permissions
/// </summary>
public IActionResult Edit<T>(T data, bool typed = true, Action<EditModel<T>> transform = null) where T : IZeroIdEntity
public EditModel<T> Edit<T>(T data, bool typed = true, Action<EditModel<T>> transform = null) where T : IZeroIdEntity
{
Type type = typeof(T);
bool canBeShared = AppAwareShareableType.IsAssignableFrom(type);
@@ -46,7 +42,7 @@ namespace zero.Web.Controllers
if (data == null)
{
return NotFound();
return null;
}
EditModel<T> model = new EditModel<T>();
@@ -61,14 +57,14 @@ namespace zero.Web.Controllers
transform?.Invoke(model);
return Json(model, typed);
return model;
}
/// <summary>
/// Creates an edit model with appropriate options and permissions
/// </summary>
public IActionResult Edit<T, TWrapper>(TWrapper data, bool typed = true, Action<EditModel<T>> transform = null)
public TWrapper Edit<T, TWrapper>(TWrapper data, bool typed = true, Action<EditModel<T>> transform = null)
where T : IZeroIdEntity
where TWrapper : EditModel<T>, new()
{
@@ -87,11 +83,11 @@ namespace zero.Web.Controllers
transform?.Invoke(data);
return Json(data, typed);
return data;
}
public IActionResult JsonPreviews<T>(Dictionary<string, T> items, Func<T, PreviewModel> transform)
public IList<PreviewModel> Previews<T>(Dictionary<string, T> items, Func<T, PreviewModel> transform)
{
IList<PreviewModel> previews = new List<PreviewModel>();
@@ -116,12 +112,12 @@ namespace zero.Web.Controllers
}
}
return Json(previews);
return previews;
}
public async Task<IActionResult> JsonPreviews<T>(Dictionary<string, T> items, Func<T, Task<PreviewModel>> transform)
public async Task<IList<PreviewModel>> Previews<T>(Dictionary<string, T> items, Func<T, Task<PreviewModel>> transform)
{
IList<PreviewModel> previews = new List<PreviewModel>();
@@ -146,7 +142,7 @@ namespace zero.Web.Controllers
}
}
return Json(previews);
return previews;
}
+12 -12
View File
@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Mvc;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@@ -7,6 +8,7 @@ using zero.Core.Api;
using zero.Core.Entities;
using zero.Core.Extensions;
using zero.Core.Identity;
using zero.Web.Models;
namespace zero.Web.Controllers
{
@@ -14,35 +16,33 @@ namespace zero.Web.Controllers
public class CountriesController : BackofficeController
{
ICountriesApi Api;
ICountry Blueprint;
public CountriesController(ICountriesApi api, ICountry blueprint)
public CountriesController(ICountriesApi api)
{
Api = api;
Blueprint = blueprint;
}
public async Task<IActionResult> GetById([FromQuery] string id) => Edit(await Api.GetById(id));
public async Task<EditModel<ICountry>> GetById([FromQuery] string id) => Edit(await Api.GetById(id));
public IActionResult GetEmpty() => Edit(Blueprint.Clone());
public EditModel<ICountry> GetEmpty([FromServices] ICountry blueprint) => Edit(blueprint);
public async Task<IActionResult> GetAll([FromQuery] ListQuery<ICountry> query) => Json(await Api.GetByQuery("languages.1-A", query)); // TODO correct language
public async Task<ListResult<ICountry>> GetAll([FromQuery] ListQuery<ICountry> query) => await Api.GetByQuery("languages.1-A", query); // TODO correct language
public async Task<IActionResult> GetForPicker() => Json((await Api.GetAll("languages.1-A")).Select(x => new SelectModel()
public async Task<IEnumerable<SelectModel>> GetForPicker() => (await Api.GetAll("languages.1-A")).Select(x => new SelectModel()
{
Id = x.Id,
Name = x.Name,
IsActive = x.IsActive
}));
});
public async Task<IActionResult> GetPreviews(List<string> ids)
public async Task<IList<PreviewModel>> GetPreviews(List<string> ids)
{
return JsonPreviews(await Api.GetByIds(ids.ToArray()), item => new PreviewModel()
return Previews(await Api.GetByIds(ids.ToArray()), item => new PreviewModel()
{
Id = item.Id,
Icon = "flag flag-" + item.Code.ToLowerInvariant(),
@@ -52,10 +52,10 @@ namespace zero.Web.Controllers
[ZeroAuthorize(Permissions.Settings.Countries, PermissionsValue.Update)]
public async Task<IActionResult> Save([FromBody] ICountry model) => Json(await Api.Save(model));
public async Task<EntityResult<ICountry>> Save([FromBody] ICountry model) => await Api.Save(model);
[ZeroAuthorize(Permissions.Settings.Countries, PermissionsValue.Update)]
public async Task<IActionResult> Delete([FromQuery] string id) => Json(await Api.Delete(id));
public async Task<EntityResult<ICountry>> Delete([FromQuery] string id) => await Api.Delete(id);
}
}
-32
View File
@@ -1,32 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using zero.Core.Extensions;
using zero.Core.Identity;
using zero.Web.Models;
namespace zero.Web.Controllers
{
[ZeroAuthorize(false)]
public class IndexController : BackofficeController
{
private IZeroVue ZeroVue { get; set; }
public IndexController(IZeroVue zeroVue)
{
ZeroVue = zeroVue;
}
public IActionResult Index()
{
if (Options.ZeroVersion.IsNullOrEmpty())
{
return RedirectToAction("Index", "Setup");
}
return View(new ZeroBackofficeModel()
{
Vue = ZeroVue
});
}
}
}
+13 -36
View File
@@ -4,8 +4,8 @@ using System.Linq;
using System.Threading.Tasks;
using zero.Core.Api;
using zero.Core.Entities;
using zero.Core.Extensions;
using zero.Core.Identity;
using zero.Web.Models;
namespace zero.Web.Controllers
{
@@ -13,56 +13,39 @@ namespace zero.Web.Controllers
public class LanguagesController : BackofficeController
{
ILanguagesApi Api;
ILanguage Blueprint;
public LanguagesController(ILanguagesApi api, ILanguage blueprint)
public LanguagesController(ILanguagesApi api)
{
Api = api;
Blueprint = blueprint;
}
/// <summary>
/// Get empty language model
/// </summary>
public IActionResult GetEmpty() => Edit(Blueprint.Clone());
public EditModel<ILanguage> GetEmpty([FromServices] ILanguage blueprint) => Edit(blueprint);
/// <summary>
/// Get language by id
/// </summary>
public async Task<IActionResult> GetById([FromQuery] string id) => Edit(await Api.GetById(id));
public async Task<EditModel<ILanguage>> GetById([FromQuery] string id) => Edit(await Api.GetById(id));
/// <summary>
/// Get all languages
/// </summary>
public async Task<IActionResult> GetAll([FromQuery] ListQuery<ILanguage> query) => Json(await Api.GetByQuery(query));
public async Task<ListResult<ILanguage>> GetAll([FromQuery] ListQuery<ILanguage> query) => await Api.GetByQuery(query);
/// <summary>
/// Returns all cultures available for creating languages.
/// </summary>
public IActionResult GetAllCultures() => Json(Api.GetAllCultures());
public IList<Culture> GetAllCultures() => Api.GetAllCultures();
/// <summary>
/// Returns all available backoffice cultures.
/// </summary>
public IActionResult GetSupportedCultures() => Json(Api.GetAllCultures(Options.SupportedLanguages));
public IList<Culture> GetSupportedCultures() => Api.GetAllCultures(Options.SupportedLanguages);
public async Task<IActionResult> GetForPicker() => Json((await Api.GetAll()).Select(x => new SelectModel()
public async Task<IEnumerable<SelectModel>> GetForPicker() => (await Api.GetAll()).Select(x => new SelectModel()
{
Id = x.Id,
Name = x.Name,
IsActive = x.IsActive
}));
});
public async Task<IActionResult> GetPreviews(List<string> ids)
public async Task<IList<PreviewModel>> GetPreviews(List<string> ids)
{
return JsonPreviews(await Api.GetByIds(ids.ToArray()), item => new PreviewModel()
return Previews(await Api.GetByIds(ids.ToArray()), item => new PreviewModel()
{
Id = item.Id,
Icon = "fth-globe",
@@ -71,17 +54,11 @@ namespace zero.Web.Controllers
}
/// <summary>
/// Save language
/// </summary>
[ZeroAuthorize(Permissions.Settings.Languages, PermissionsValue.Update)]
public async Task<IActionResult> Save([FromBody] ILanguage model) => Json(await Api.Save(model));
public async Task<EntityResult<ILanguage>> Save([FromBody] ILanguage model) => await Api.Save(model);
/// <summary>
/// Deletes a language
/// </summary>
[ZeroAuthorize(Permissions.Settings.Languages, PermissionsValue.Update)]
public async Task<IActionResult> Delete([FromQuery] string id) => Json(await Api.Delete(id));
public async Task<EntityResult<ILanguage>> Delete([FromQuery] string id) => await Api.Delete(id);
}
}
+10 -10
View File
@@ -31,32 +31,32 @@ namespace zero.Web.Controllers
}
public async Task<IActionResult> GetById([FromQuery] string id) => Edit(await Api.GetById(id));
public async Task<EditModel<IMedia>> GetById([FromQuery] string id) => Edit(await Api.GetById(id));
public async Task<IActionResult> GetByIds([FromQuery] string[] ids) => Json(await Api.GetById(ids));
public async Task<Dictionary<string, IMedia>> GetByIds([FromQuery] string[] ids) => await Api.GetById(ids);
public async Task<IActionResult> GetListByQuery([FromQuery] MediaListItemQuery query) => Json(await Api.GetListByQuery(query));
public async Task<ListResult<MediaListItem>> GetListByQuery([FromQuery] MediaListItemQuery query) => await Api.GetListByQuery(query);
public async Task<IActionResult> Save([FromBody] IMedia model) => Json(await Api.Save(model));
public async Task<EntityResult<IMedia>> Save([FromBody] IMedia model) => await Api.Save(model);
public async Task<IActionResult> Upload(IFormFile file, string folderId) => Json(await Api.Save(await Api.Upload(file, folderId)));
public async Task<EntityResult<IMedia>> Upload(IFormFile file, string folderId) => await Api.Save(await Api.Upload(file, folderId));
public async Task<IActionResult> UploadTemporary(IFormFile file, string folderId) => Json(await Api.Upload(file, folderId));
public async Task<Media> UploadTemporary(IFormFile file, string folderId) => await Api.Upload(file, folderId);
public async Task<IActionResult> Delete([FromQuery] string id) => Json(await Api.Delete(id));
public async Task<EntityResult<IMedia>> Delete([FromQuery] string id) => await Api.Delete(id);
[HttpPost]
public async Task<IActionResult> Move([FromBody] ActionCopyModel model) => Json(await Api.Move(model.Id, model.DestinationId));
public async Task<EntityResult<IMedia>> Move([FromBody] ActionCopyModel model) => await Api.Move(model.Id, model.DestinationId);
public async Task<IActionResult> GetAll([FromQuery] MediaListQuery query)
public async Task<MediaListResultModel> GetAll([FromQuery] MediaListQuery query)
{
ListResult<MediaListModel> items = (await Api.GetByQuery(query)).MapTo(x => new MediaListModel()
{
@@ -77,7 +77,7 @@ namespace zero.Web.Controllers
hierarchy = await MediaFolderApi.GetHierarchy(query.FolderId);
}
return Json(new MediaListResultModel(items, null, folder, hierarchy));
return new MediaListResultModel(items, null, folder, hierarchy);
}
@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
using zero.Core.Api;
using zero.Core.Entities;
@@ -18,25 +19,25 @@ namespace zero.Web.Controllers
}
public async Task<IActionResult> GetById([FromQuery] string id) => Edit(await Api.GetById(id));
public async Task<EditModel<IMediaFolder>> GetById([FromQuery] string id) => Edit(await Api.GetById(id));
public IActionResult GetEmpty([FromServices] IMediaFolder blueprint) => Edit(blueprint);
public EditModel<IMediaFolder> GetEmpty([FromServices] IMediaFolder blueprint) => Edit(blueprint);
public async Task<IActionResult> GetHierarchy([FromQuery] string id) => Json(await Api.GetHierarchy(id));
public async Task<IList<IMediaFolder>> GetHierarchy([FromQuery] string id) => await Api.GetHierarchy(id);
public async Task<IActionResult> GetAllAsTree([FromQuery] string parent = null, [FromQuery] string active = null) => Json(await Api.GetAllAsTree(parent, active));
public async Task<IList<TreeItem>> GetAllAsTree([FromQuery] string parent = null, [FromQuery] string active = null) => await Api.GetAllAsTree(parent, active);
[HttpPost]
public async Task<IActionResult> Move([FromBody] ActionCopyModel model) => Json(await Api.Move(model.Id, model.DestinationId));
public async Task<EntityResult<IMediaFolder>> Move([FromBody] ActionCopyModel model) => await Api.Move(model.Id, model.DestinationId);
public async Task<IActionResult> Save([FromBody] IMediaFolder model) => Json(await Api.Save(model));
public async Task<EntityResult<IMediaFolder>> Save([FromBody] IMediaFolder model) => await Api.Save(model);
public async Task<IActionResult> Delete([FromQuery] string id) => Json(await Api.Delete(id));
public async Task<EntityResult<IMediaFolder>> Delete([FromQuery] string id) => await Api.Delete(id);
}
}
+7 -4
View File
@@ -1,9 +1,10 @@
using Microsoft.AspNetCore.Mvc;
using System;
using System.Linq;
using System.Collections.Generic;
using zero.Core.Api;
using zero.Core.Entities;
using zero.Core.Utils;
using zero.Web.Models;
namespace zero.Web.Controllers
{
@@ -17,11 +18,13 @@ namespace zero.Web.Controllers
}
public IActionResult GetModuleTypes([FromQuery] string[] tags = default) => Json(Api.GetModuleTypes(tags));
public IList<ModuleType> GetModuleTypes([FromQuery] string[] tags = default) => Api.GetModuleTypes(tags);
public IActionResult GetModuleType([FromQuery] string alias) => Json(Api.GetModuleType(alias));
public IActionResult GetEmpty(string alias)
public ModuleType GetModuleType([FromQuery] string alias) => Api.GetModuleType(alias);
public EditModel<IModule> GetEmpty(string alias)
{
ModuleType moduleType = Api.GetModuleType(alias);
IModule module = Activator.CreateInstance(moduleType.ContentType) as IModule;
+3 -3
View File
@@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
using zero.Core.Api;
using zero.Core.Entities;
@@ -15,9 +15,9 @@ namespace zero.Web.Controllers
}
public async Task<IActionResult> GetChildren(string parent = null, string active = null)
public async Task<IList<TreeItem>> GetChildren(string parent = null, string active = null)
{
return Json(await Api.GetChildren(parent, active));
return await Api.GetChildren(parent, active);
}
}
}
+21 -12
View File
@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using zero.Core.Api;
using zero.Core.Entities;
@@ -20,18 +21,18 @@ namespace zero.Web.Controllers
}
public async Task<ActionResult> GetAllowedPageTypes([FromQuery] string parent = null) => Json(await Api.GetAllowedPageTypes(parent));
public async Task<IList<PageType>> GetAllowedPageTypes([FromQuery] string parent = null) => await Api.GetAllowedPageTypes(parent);
public IActionResult GetPageType([FromQuery] string alias) => Json(Api.GetPageType(alias));
public PageType GetPageType([FromQuery] string alias) => Api.GetPageType(alias);
public async Task<IActionResult> GetById([FromQuery] string id)
public async Task<PageEditModel<IPage>> GetById([FromQuery] string id)
{
IPage entity = await Api.GetById(id);
if (entity == null)
{
return NotFound();
return null;
}
return Edit<IPage, PageEditModel<IPage>>(new PageEditModel<IPage>()
@@ -42,7 +43,8 @@ namespace zero.Web.Controllers
});
}
public IActionResult GetEmpty(string type, string parent = null)
public EditModel<IPage> GetEmpty(string type, string parent = null)
{
PageType pageType = Api.GetPageType(type);
IPage model = Activator.CreateInstance(pageType.ContentType) as IPage;
@@ -53,22 +55,29 @@ namespace zero.Web.Controllers
return Edit(model);
}
public async Task<IActionResult> GetRevisions([FromQuery] string id, [FromQuery] int page = 1) => Json(await RevisionsApi.GetPaged<IPage>(id, page));
public async Task<IActionResult> Save([FromBody] IPage model) => Json(await Api.Save(model));
public async Task<ListResult<Revision>> GetRevisions([FromQuery] string id, [FromQuery] int page = 1) => await RevisionsApi.GetPaged<IPage>(id, page);
public async Task<EntityResult<IPage>> Save([FromBody] IPage model) => await Api.Save(model);
[HttpPost]
public async Task<IActionResult> SaveSorting([FromBody] string[] ids) => Json(await Api.SaveSorting(ids));
public async Task<EntityResult<IList<IPage>>> SaveSorting([FromBody] string[] ids) => await Api.SaveSorting(ids);
[HttpPost]
public async Task<IActionResult> Move([FromBody] ActionCopyModel model) => Json(await Api.Move(model.Id, model.DestinationId));
public async Task<EntityResult<IPage>> Move([FromBody] ActionCopyModel model) => await Api.Move(model.Id, model.DestinationId);
[HttpPost]
public async Task<IActionResult> Copy([FromBody] ActionCopyModel model) => Json(await Api.Copy(model.Id, model.DestinationId, model.IncludeDescendants));
public async Task<EntityResult<IPage>> Copy([FromBody] ActionCopyModel model) => await Api.Copy(model.Id, model.DestinationId, model.IncludeDescendants);
[HttpPost]
public async Task<IActionResult> Restore([FromBody] ActionCopyModel model) => Json(await Api.Restore(model.Id, model.IncludeDescendants));
public async Task<EntityResult<string[]>> Restore([FromBody] ActionCopyModel model) => await Api.Restore(model.Id, model.IncludeDescendants);
public async Task<IActionResult> Delete([FromQuery] string id) => Json(await Api.Delete(id, true));
public async Task<EntityResult<string[]>> Delete([FromQuery] string id) => await Api.Delete(id, true);
}
}
+6 -5
View File
@@ -2,6 +2,7 @@
using System.Threading.Tasks;
using zero.Core.Api;
using zero.Core.Entities;
using zero.Web.Models;
namespace zero.Web.Controllers
{
@@ -15,20 +16,20 @@ namespace zero.Web.Controllers
}
public async Task<IActionResult> GetById([FromQuery] string id) => Edit(await Api.GetById(id));
public async Task<EditModel<IPreview>> GetById([FromQuery] string id) => Edit(await Api.GetById(id));
public async Task<IActionResult> Add([FromBody] IZeroEntity model)
public async Task<EntityResult<string>> Add([FromBody] IZeroEntity model)
{
EntityResult<IPreview> preview = await Api.Add(model);
return Json(EntityResult<string>.From(preview, preview.Model?.Id));
return EntityResult<string>.From(preview, preview.Model?.Id);
}
public async Task<IActionResult> Update([FromBody] string id, [FromBody] IZeroEntity model)
public async Task<EntityResult<string>> Update([FromQuery] string id, [FromBody] IZeroEntity model)
{
EntityResult<IPreview> preview = await Api.Update(id, model);
return Json(EntityResult<string>.From(preview, id));
return EntityResult<string>.From(preview, id);
}
}
}
+9 -8
View File
@@ -1,11 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using zero.Core;
using zero.Core.Api;
using zero.Core.Entities;
using zero.Core.Identity;
namespace zero.Web.Controllers
{
@@ -18,14 +14,19 @@ namespace zero.Web.Controllers
Api = api;
}
public async Task<IActionResult> GetByQuery([FromQuery] RecycleBinListQuery query) => Json(await Api.GetByQuery(query));
public async Task<IActionResult> GetCountByOperation([FromQuery] string operationId) => Json(await Api.GetCountByOperation(operationId));
public async Task<ListResult<IRecycledEntity>> GetByQuery([FromQuery] RecycleBinListQuery query) => await Api.GetByQuery(query);
public async Task<int> GetCountByOperation([FromQuery] string operationId) => await Api.GetCountByOperation(operationId);
[HttpDelete]
public async Task<IActionResult> Delete([FromQuery] string id) => Json(await Api.Delete(id));
public async Task<EntityResult<IRecycledEntity>> Delete([FromQuery] string id) => await Api.Delete(id);
[HttpDelete]
public async Task<IActionResult> DeleteByGroup([FromQuery] string group) => Json(await Api.DeleteByGroup(group));
public async Task<EntityResult<IRecycledEntity>> DeleteByGroup([FromQuery] string group) => await Api.DeleteByGroup(group);
}
}
+3 -5
View File
@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using zero.Core.Api;
using zero.Core.Entities;
namespace zero.Web.Controllers
{
@@ -13,9 +14,6 @@ namespace zero.Web.Controllers
}
public IActionResult GetAll()
{
return Json(Api.GetAll());
}
public IReadOnlyCollection<ISection> GetAll() => Api.GetAll();
}
}
+4 -5
View File
@@ -1,5 +1,4 @@
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Threading.Tasks;
using zero.Core;
using zero.Core.Api;
@@ -28,7 +27,7 @@ namespace zero.Web.Controllers
/// <summary>
/// Get all settings areas
/// </summary>
public async Task<IActionResult> GetAreas()
public async Task<dynamic> GetAreas()
{
bool isSuperUser = AuthApi.IsSuper();
IList<Permission> permissions = AuthApi.GetPermissions(Permissions.Settings.PREFIX);
@@ -75,11 +74,11 @@ namespace zero.Web.Controllers
applications = await ApplicationsApi.GetAll();
}
return Json(new
return new
{
groups,
applications
});
};
}
}
}
+6 -22
View File
@@ -21,9 +21,6 @@ namespace zero.Web.Controllers
}
/// <summary>
/// Get space by alias
/// </summary>
public IActionResult GetByAlias([FromQuery] string alias)
{
if (!CanReadSpace(alias))
@@ -31,23 +28,13 @@ namespace zero.Web.Controllers
return new StatusCodeResult(403);
}
return Json(Api.GetByAlias(alias));
return Ok(Api.GetByAlias(alias));
}
/// <summary>
/// Get all spaces
/// </summary>
public IActionResult GetAll()
{
IList<Space> spaces = Api.GetAll().Where(space => CanReadSpace(space.Alias)).ToList();
return Json(spaces);
}
public List<Space> GetAll() => Api.GetAll().Where(space => CanReadSpace(space.Alias)).ToList();
/// <summary>
/// Get list items in a space
/// </summary>
public async Task<IActionResult> GetList([FromQuery] string alias, [FromQuery] ListQuery<ISpaceContent> query = null)
{
if (!CanReadSpace(alias))
@@ -55,13 +42,10 @@ namespace zero.Web.Controllers
return new StatusCodeResult(403);
}
return Json(await Api.GetListByQuery(alias, query));
return Ok(await Api.GetListByQuery(alias, query));
}
/// <summary>
/// Get list items in a space
/// </summary>
public async Task<IActionResult> GetContent([FromQuery] string alias, [FromQuery] string contentId = null)
{
if (!CanReadSpace(alias))
@@ -88,7 +72,7 @@ namespace zero.Web.Controllers
model.SpaceAlias = space.Alias;
}
return Edit(model);
return Ok(Edit(model));
}
@@ -102,7 +86,7 @@ namespace zero.Web.Controllers
return new StatusCodeResult(403);
}
return Json(await Api.Save(model));
return Ok(await Api.Save(model));
}
@@ -116,7 +100,7 @@ namespace zero.Web.Controllers
return new StatusCodeResult(403);
}
return Json(await Api.Delete(id));
return Ok(await Api.Delete(id));
}
+7 -24
View File
@@ -2,8 +2,8 @@
using System.Threading.Tasks;
using zero.Core.Api;
using zero.Core.Entities;
using zero.Core.Extensions;
using zero.Core.Identity;
using zero.Web.Models;
namespace zero.Web.Controllers
{
@@ -11,44 +11,27 @@ namespace zero.Web.Controllers
public class TranslationsController : BackofficeController
{
ITranslationsApi Api;
ITranslation Blueprint;
public TranslationsController(ITranslationsApi api, ITranslation blueprint)
public TranslationsController(ITranslationsApi api)
{
Api = api;
Blueprint = blueprint;
}
/// <summary>
/// Get translation by id
/// </summary>
public IActionResult GetEmpty() => Edit(Blueprint.Clone());
public EditModel<ITranslation> GetEmpty([FromServices] ITranslation blueprint) => Edit(blueprint);
/// <summary>
/// Get translation by id
/// </summary>
public async Task<IActionResult> GetById([FromQuery] string id) => Edit(await Api.GetById(id));
public async Task<EditModel<ITranslation>> GetById([FromQuery] string id) => Edit(await Api.GetById(id));
/// <summary>
/// Get all translations
/// </summary>
public async Task<IActionResult> GetAll([FromQuery] ListQuery<ITranslation> query) => Json(await Api.GetByQuery(query));
public async Task<ListResult<ITranslation>> GetAll([FromQuery] ListQuery<ITranslation> query) => await Api.GetByQuery(query);
/// <summary>
/// Save translation
/// </summary>
[ZeroAuthorize(Permissions.Settings.Translations, PermissionsValue.Update)]
public async Task<IActionResult> Save([FromBody] ITranslation model) => Json(await Api.Save(model));
public async Task<EntityResult<ITranslation>> Save([FromBody] ITranslation model) => await Api.Save(model);
/// <summary>
/// Deletes a translation
/// </summary>
[ZeroAuthorize(Permissions.Settings.Translations, PermissionsValue.Update)]
public async Task<IActionResult> Delete([FromQuery] string id) => Json(await Api.Delete(id));
public async Task<EntityResult<ITranslation>> Delete([FromQuery] string id) => await Api.Delete(id);
}
}
+7 -5
View File
@@ -1,8 +1,10 @@
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;
using zero.Core.Api;
using zero.Core.Entities;
using zero.Core.Identity;
using zero.Web.Models;
namespace zero.Web.Controllers
{
@@ -20,20 +22,20 @@ namespace zero.Web.Controllers
}
public async Task<IActionResult> GetById([FromQuery] string id) => Edit(await Api.GetById(id));
public async Task<EditModel<IUserRole>> GetById([FromQuery] string id) => Edit(await Api.GetById(id));
public async Task<IActionResult> GetAll() => Json(await Api.GetAll());
public async Task<IList<IUserRole>> GetAll() => await Api.GetAll();
public IActionResult GetAllPermissions() => Json(PermissionsApi.GetAll());
public IList<PermissionCollection> GetAllPermissions() => PermissionsApi.GetAll();
[ZeroAuthorize(Permissions.Settings.Users, PermissionsValue.Update)]
public async Task<IActionResult> Save([FromBody] IUserRole model) => Json(await Api.Save(model));
public async Task<EntityResult<IUserRole>> Save([FromBody] IUserRole model) => await Api.Save(model);
[ZeroAuthorize(Permissions.Settings.Users, PermissionsValue.Update)]
public async Task<IActionResult> Delete([FromQuery] string id) => Json(await Api.Delete(id));
public async Task<EntityResult<IUserRole>> Delete([FromQuery] string id) => await Api.Delete(id);
}
}
+16 -16
View File
@@ -25,26 +25,26 @@ namespace zero.Web.Controllers
}
public async Task<IActionResult> GetById([FromQuery] string id) => Edit(await Api.GetUserById(id));
public async Task<EditModel<IUser>> GetById([FromQuery] string id) => Edit(await Api.GetUserById(id));
public async Task<IActionResult> GetAll([FromQuery] ListQuery<IUser> query) => Json(await Api.GetByQuery(query));
public async Task<ListResult<IUser>> GetAll([FromQuery] ListQuery<IUser> query) => await Api.GetByQuery(query);
public IActionResult GetAllPermissions() => Json(PermissionsApi.GetAll());
public IList<PermissionCollection> GetAllPermissions() => PermissionsApi.GetAll();
public async Task<IActionResult> GetForPicker() => Json((await Api.GetAll()).Select(x => new SelectModel()
public async Task<IEnumerable<SelectModel>> GetForPicker() => (await Api.GetAll()).Select(x => new SelectModel()
{
Id = x.Id,
Name = x.Name,
IsActive = x.IsActive
}));
});
public async Task<IActionResult> GetPreviews(List<string> ids)
public async Task<IList<PreviewModel>> GetPreviews(List<string> ids)
{
return JsonPreviews(await Api.GetByIds(ids.ToArray()), item => new PreviewModel()
return Previews(await Api.GetByIds(ids.ToArray()), item => new PreviewModel()
{
Id = item.Id,
Icon = item.AvatarId,
@@ -54,7 +54,7 @@ namespace zero.Web.Controllers
[ZeroAuthorize]
public async Task<IActionResult> UpdatePassword([FromBody] UserPasswordEditModel model)
public async Task<EntityResult<IUser>> UpdatePassword([FromBody] UserPasswordEditModel model)
{
EntityResult<IUser> result;
@@ -73,36 +73,36 @@ namespace zero.Web.Controllers
}
}
return Json(result);
return result;
}
[ZeroAuthorize(Permissions.Settings.Users, PermissionsValue.Update)]
public async Task<IActionResult> Disable([FromBody] IUser model)
public async Task<EntityResult<IUser>> Disable([FromBody] IUser model)
{
IUser entity = await Api.GetUserById(model.Id);
return Json(await Api.Disable(entity));
return await Api.Disable(entity);
}
[ZeroAuthorize(Permissions.Settings.Users, PermissionsValue.Update)]
public async Task<IActionResult> Enable([FromBody] IUser model)
public async Task<EntityResult<IUser>> Enable([FromBody] IUser model)
{
IUser entity = await Api.GetUserById(model.Id);
return Json(await Api.Enable(entity));
return await Api.Enable(entity);
}
[ZeroAuthorize(Permissions.Settings.Users, PermissionsValue.Update)]
public async Task<IActionResult> Save([FromBody] IUser model) => Json(await Api.Save(model));
public async Task<EntityResult<IUser>> Save([FromBody] IUser model) => await Api.Save(model);
[ZeroAuthorize(Permissions.Settings.Users, PermissionsValue.Update)]
// TODO do not need settings.users authorization for editing current user profiles
public async Task<IActionResult> SaveCurrent([FromBody] IUser model) => Json(await Api.Save(model));
public async Task<EntityResult<IUser>> SaveCurrent([FromBody] IUser model) => await Api.Save(model);
[ZeroAuthorize(Permissions.Settings.Users, PermissionsValue.Update)]
public async Task<IActionResult> Delete([FromQuery] string id) => Json(await Api.Delete(id));
public async Task<EntityResult<IUser>> Delete([FromQuery] string id) => await Api.Delete(id);
}
}
+1 -4
View File
@@ -5,9 +5,6 @@ namespace zero.Web.Controllers
{
public class UtilsController : BackofficeController
{
/// <summary>
/// Generate alias from name
/// </summary>
public IActionResult GenerateAlias([FromQuery] string name) => Json(Safenames.Alias(name));
public string GenerateAlias([FromQuery] string name) => Safenames.Alias(name);
}
}
@@ -0,0 +1,35 @@
using Microsoft.AspNetCore.Mvc;
using zero.Core.Extensions;
using zero.Core.Identity;
using zero.Core.Options;
using zero.Web.Models;
namespace zero.Web.Controllers
{
[ZeroAuthorize(false)]
public class ZeroBackofficeController : Controller
{
IZeroVue ZeroVue { get; set; }
IZeroOptions Options { get; set; }
public ZeroBackofficeController(IZeroVue zeroVue, IZeroOptions options)
{
ZeroVue = zeroVue;
Options = options;
}
public IActionResult Index()
{
if (Options.ZeroVersion.IsNullOrEmpty())
{
return RedirectToAction("ZeroBackoffice", "Setup");
}
return View("Views/Zero/Index.cshtml", new ZeroBackofficeModel()
{
Vue = ZeroVue
});
}
}
}
@@ -0,0 +1,33 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using System;
namespace zero.Web.Controllers
{
public class ZeroBackofficeControllerModelConvention : IControllerModelConvention
{
readonly AttributeRouteModel RouteModel;
readonly Type BaseClass = typeof(BackofficeController);
public ZeroBackofficeControllerModelConvention(string backofficePath)
{
RouteModel = new AttributeRouteModel(new RouteAttribute(backofficePath + "/api/[controller]/[action]"));
}
public void Apply(ControllerModel controller)
{
if (!controller.ControllerType.IsSubclassOf(BaseClass))
{
return;
}
foreach (var selector in controller.Selectors)
{
selector.AttributeRouteModel = RouteModel;
}
}
}
}
-4
View File
@@ -1,10 +1,6 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using zero.Core.Entities;
using zero.Core.Routing;
@@ -6,9 +6,9 @@ using zero.Core.Routing;
namespace zero.Web.Controllers
{
public class DefaultRouteController : ZeroController<PageRoute>
public class ZeroFrontendController : ZeroController<PageRoute>
{
public DefaultRouteController()
public ZeroFrontendController()
{
}
@@ -8,21 +8,23 @@ using zero.Core.Entities;
using zero.Core.Entities.Setup;
using zero.Core.Extensions;
using zero.Core.Identity;
using zero.Web.Controllers;
using zero.Core.Options;
namespace zero.Web.Setup
{
[ZeroAuthorize(false)]
public class SetupController : BackofficeController
public class ZeroSetupController : Controller
{
ISetupApi Api;
IWebHostEnvironment Env;
IZeroOptions Options;
public SetupController(ISetupApi api, IWebHostEnvironment env)
public ZeroSetupController(ISetupApi api, IWebHostEnvironment env, IZeroOptions options)
{
Api = api;
Env = env;
Options = options;
}
@@ -33,7 +35,7 @@ namespace zero.Web.Setup
return Redirect(Options.BackofficePath);
}
return View("/Views/Setup.cshtml");
return View("/Views/Zero/Setup.cshtml");
}
+2 -2
View File
@@ -19,12 +19,12 @@ namespace zero.Web.Controllers
[HttpGet]
public async Task<IActionResult> Config()
public async Task<ZeroVueConfig> Config()
{
var settings = new JsonSerializerSettings();
settings.Converters.Add(new StringEnumConverter(new CamelCaseNamingStrategy()));
return Json(await ZeroVue.Config()); //, settings);
return await ZeroVue.Config(); //, settings);
}
}
}
+15 -15
View File
@@ -40,21 +40,21 @@ namespace zero.Web
builder.UseEndpoints(endpoints =>
{
// setup route
endpoints.MapControllerRoute(
name: "setup",
pattern: path + "/setup",
defaults: new
{
controller = "Setup",
action = "Index"
}
);
//endpoints.MapControllerRoute(
// name: "setup",
// pattern: path + "/setup",
// defaults: new
// {
// controller = "ZeroSetup",
// action = "Index"
// }
//);
// routes for API
endpoints.MapControllerRoute(
name: "api",
pattern: path + "/api/{controller}/{action}/{id?}"
);
//// routes for API
//endpoints.MapControllerRoute(
// name: "api",
// pattern: path + "/api/{controller}/{action}/{id?}"
//);
if (devPath != null)
{
@@ -62,7 +62,7 @@ namespace zero.Web
}
// fallbacks for SPA
endpoints.MapFallbackToController(path + "/{**path}", "Index", "Index");
endpoints.MapFallbackToController(path + "/{**path}", "Index", "ZeroBackoffice");
});
});
+24 -6
View File
@@ -1,8 +1,10 @@
using FluentValidation;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
@@ -24,6 +26,7 @@ using zero.Core.Options;
using zero.Core.Plugins;
using zero.Core.Utils;
using zero.Core.Validation;
using zero.Web.Controllers;
using zero.Web.Defaults;
using zero.Web.Filters;
@@ -45,10 +48,7 @@ namespace zero.Web
public ZeroBuilder(IServiceCollection services, IConfiguration configuration, Action<IZeroStartupOptions> setupAction)
{
Services = services;
Mvc = services.AddMvc(opts =>
{
//opts.ModelBinderProviders.Insert(0, new ZeroEntityInterfaceBinderProvider());
});
Mvc = services.AddMvc();
Configuration = configuration;
// create startup options
@@ -80,16 +80,18 @@ namespace zero.Web
// configure MVC
Services.TryAddEnumerable(ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, ZeroBuilderMvcOptions>());
Mvc.AddNewtonsoftJson(x =>
{
// TODO this shall only be configurated for backoffice controllers
BackofficeJsonSerlializerSettings.Setup(x.SerializerSettings, true);
BackofficeJsonSerlializerSettings.Setup(x.SerializerSettings);
});
if (Environment.GetEnvironmentVariable("DOTNET_WATCH") == "1")
{
Mvc.AddRazorRuntimeCompilation();
}
}
// configure Raven + Identity
ConfigureDatabase();
@@ -254,5 +256,21 @@ namespace zero.Web
throw new Exception($"Plugin \"{nameof(T)}\" needs an additional parameterless constructor as ConfigureServices() is called before the DI container is built");
}
}
class ZeroBuilderMvcOptions : IConfigureOptions<MvcOptions>
{
IZeroOptions Options { get; set; }
public ZeroBuilderMvcOptions(IZeroOptions options)
{
Options = options;
}
public void Configure(MvcOptions options)
{
options.Conventions.Add(new ZeroBackofficeControllerModelConvention(Options.BackofficePath));
}
}
}
}
+4 -4
View File
@@ -14,12 +14,12 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.0-preview.8.20414.8" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="5.0.0-preview.8.20414.8" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.0-rc.2.20475.17" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="5.0.0-rc.2.20475.17" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="5.0.0-preview.8.20414.8" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="5.0.0-preview.8.20414.8" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="5.0.0-rc.2.20475.17" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.2.0" />
<PackageReference Include="VueCliMiddleware" Version="3.1.1" />
<PackageReference Include="VueCliMiddleware" Version="3.1.2" />
</ItemGroup>
<ItemGroup>