diff --git a/Library.Encyclopedia.API/Attributes/ApiKeyAttribute.cs b/Library.Encyclopedia.API/Attributes/ApiKeyAttribute.cs index 924a61a..a3ee159 100644 --- a/Library.Encyclopedia.API/Attributes/ApiKeyAttribute.cs +++ b/Library.Encyclopedia.API/Attributes/ApiKeyAttribute.cs @@ -7,6 +7,10 @@ namespace Library.Encyclopedia.API.Attributes { + /// + /// This is a custom attribute for API Kay validation + /// Extracts API key from the header and then checks it with the API KEY hardcoded in appsettings + /// [AttributeUsage(validOn: AttributeTargets.Class | AttributeTargets.Method)] public class ApiKeyAttribute : Attribute, IAsyncActionFilter { diff --git a/Library.Encyclopedia.API/Controllers/EncylopediaController.cs b/Library.Encyclopedia.API/Controllers/EncylopediaController.cs index 68b3d27..023d81a 100644 --- a/Library.Encyclopedia.API/Controllers/EncylopediaController.cs +++ b/Library.Encyclopedia.API/Controllers/EncylopediaController.cs @@ -14,26 +14,37 @@ namespace Library.Encyclopedia.Controllers { /// - /// CRUD operations on Main Table + /// Controller to perform CRUD operations on Main Table /// [ApiController] [Route("[controller]")] public class EncylopediaController : ControllerBase { + #region PRIVATE FIELDS private readonly ILogger _logger; private readonly IMainDataAccess mainDataAccess; + #endregion + #region CONSTRUCTOR /// - /// Constructor + /// Contructor for EncylopediaController /// - /// - /// + /// logger instance + /// db instance + /// appsettings configuration + /// file upload/download/delete layer public EncylopediaController(ILogger logger, IApplicationDbContext dbContext, IConfiguration configuration, IFilesAdapter filesAdapter) { _logger = logger; this.mainDataAccess = new MainDataAccess(dbContext, configuration, filesAdapter); - } + } + #endregion + #region LOGIN + /// + /// Login to perform POST/PUT/DELETE operations on the database + /// + /// [HttpGet("Login")] [Authorize(Roles = @"EncyclopediaAdministrators")] public IActionResult Login() @@ -53,7 +64,9 @@ public IActionResult Login() throw; } } + #endregion + #region GET /// /// Get all items based on search query /// @@ -185,21 +198,19 @@ public async Task Get(Guid id) } /// - /// Create new entry + /// Get all categories /// /// - [HttpPost] - //[ApiKey] - [Authorize(Roles = @"EncyclopediaAdministrators")] - public async Task Create([FromBody]Main model) + [HttpGet("fetchallcategories")] + public async Task GetAllCategories() { try { - var response = await mainDataAccess.CreateAsync(model); + var response = await mainDataAccess.GetAllCategoriesAsync(); if (response == null) { - return StatusCode(500); + return StatusCode(204); } else { @@ -212,19 +223,21 @@ public async Task Create([FromBody]Main model) throw; } } + #endregion + #region POST /// /// Create new entry /// /// - [HttpPut("{id}")] + [HttpPost] //[ApiKey] [Authorize(Roles = @"EncyclopediaAdministrators")] - public async Task Update(Guid id, [FromBody]MainUpdateModel model) + public async Task Create([FromBody]Main model) { try { - var response = await mainDataAccess.UpdateAsync(id, model); + var response = await mainDataAccess.CreateAsync(model); if (response == null) { @@ -241,21 +254,30 @@ public async Task Update(Guid id, [FromBody]MainUpdateModel model throw; } } + #endregion + #region PUT /// /// Create new entry /// /// - [HttpDelete("{id}")] + [HttpPut("{id}")] //[ApiKey] [Authorize(Roles = @"EncyclopediaAdministrators")] - public async Task Delete(Guid id) + public async Task Update(Guid id, [FromBody]MainUpdateModel model) { try { - await mainDataAccess.DeleteAsync(id); + var response = await mainDataAccess.UpdateAsync(id, model); - return Ok(); + if (response == null) + { + return StatusCode(500); + } + else + { + return Ok(response); + } } catch (Exception ex) { @@ -263,32 +285,30 @@ public async Task Delete(Guid id) throw; } } + #endregion + #region DELETE /// - /// Get all categories + /// Create new entry /// /// - [HttpGet("fetchallcategories")] - public async Task GetAllCategories() + [HttpDelete("{id}")] + //[ApiKey] + [Authorize(Roles = @"EncyclopediaAdministrators")] + public async Task Delete(Guid id) { try { - var response = await mainDataAccess.GetAllCategoriesAsync(); + await mainDataAccess.DeleteAsync(id); - if (response == null) - { - return StatusCode(204); - } - else - { - return Ok(response); - } + return Ok(); } catch (Exception ex) { _logger.LogError(ex, $"an error has occured {ex.Message}"); throw; } - } + } + #endregion } } \ No newline at end of file diff --git a/Library.Encyclopedia.API/Controllers/FilesController.cs b/Library.Encyclopedia.API/Controllers/FilesController.cs index acfcb17..83c2610 100644 --- a/Library.Encyclopedia.API/Controllers/FilesController.cs +++ b/Library.Encyclopedia.API/Controllers/FilesController.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; @@ -21,12 +22,14 @@ public class FilesController : ControllerBase { private readonly ILogger _logger; private readonly IFilesAdapter filesAdapter; + private readonly IConfiguration configuration; private readonly IFilesDataAccess filesDataAccess; - public FilesController(ILogger logger, IApplicationDbContext dbContext, IFilesAdapter filesAdapter) + public FilesController(ILogger logger, IApplicationDbContext dbContext, IFilesAdapter filesAdapter, IConfiguration configuration) { _logger = logger; this.filesAdapter = filesAdapter; + this.configuration = configuration; this.filesDataAccess = new FilesDataAccess(dbContext); } @@ -77,7 +80,7 @@ public async Task Post(Guid id, IFormFile file, string descriptio } /// - /// Upload Files + /// Download Files /// /// /// @@ -100,6 +103,55 @@ public IActionResult Get(string id, string name, string contentType) } } + /// + /// Create Viewable link + /// + /// + /// + /// + /// + /// + [HttpGet("CreateViewableLink/{id}")] + [Authorize(Roles = @"EncyclopediaAdministrators")] + public IActionResult CreateViewableLink(string id, string name) + { + try + { + this.filesAdapter.CreateViewableLink(id, name); + var baseUrl = configuration.GetSection("TempAssetsBaseUrl").Value; + return Ok(baseUrl + '/' + id + '/' + name); + } + catch (Exception ex) + { + _logger.LogError(ex, $"an error has occured {ex.Message}"); + throw; + } + } + + /// + /// Destroy Viewable link + /// + /// + /// + /// + /// + /// + [HttpDelete("DestroyViewableLink/{id}")] + [Authorize(Roles = @"EncyclopediaAdministrators")] + public IActionResult DestroyViewableLink(string id, string name) + { + try + { + this.filesAdapter.DestroyViewableLink(id, name); + return Ok(); + } + catch (Exception ex) + { + _logger.LogError(ex, $"an error has occured {ex.Message}"); + throw; + } + } + /// /// Delete File /// diff --git a/Library.Encyclopedia.API/Startup.cs b/Library.Encyclopedia.API/Startup.cs index c90cbf6..a87594b 100644 --- a/Library.Encyclopedia.API/Startup.cs +++ b/Library.Encyclopedia.API/Startup.cs @@ -46,24 +46,26 @@ public void ConfigureServices(IServiceCollection services) string userName = Configuration.GetValue("FileAdapterSettings:username"); string password = Configuration.GetValue("FileAdapterSettings:password"); string basePath = Configuration.GetValue("FileAdapterSettings:basePath"); + string tempAssetsFilePath = Configuration.GetValue("TempAssetsLocation"); services.AddSingleton(s => { - return new LinuxFileSaveAdapter(url, userName, password, basePath); + return new LinuxFileSaveAdapter(url, userName, password, basePath, tempAssetsFilePath); }); } - else if(Configuration.GetValue("FileAdapterSettings:type").Equals("windows", System.StringComparison.OrdinalIgnoreCase)) + else if (Configuration.GetValue("FileAdapterSettings:type").Equals("windows", System.StringComparison.OrdinalIgnoreCase)) { string filePath = Configuration.GetValue("FileAdapterSettings:filePath"); + string tempAssetsFilePath = Configuration.GetValue("TempAssetsLocation"); services.AddSingleton(s => { - return new WindowsFileSaveAdapter(filePath); + return new WindowsFileSaveAdapter(filePath, tempAssetsFilePath); }); } services.AddScoped(s => new ApplicationDbContext(Configuration.GetConnectionString("DefaultConnection"))); - + services.Configure(x => { x.ValueLengthLimit = int.MaxValue; diff --git a/Library.Encyclopedia.API/appsettings.json b/Library.Encyclopedia.API/appsettings.json index 50bf267..bfac7f4 100644 --- a/Library.Encyclopedia.API/appsettings.json +++ b/Library.Encyclopedia.API/appsettings.json @@ -14,17 +14,19 @@ "DefaultConnection": "Server=localhost;Database=Encyclopedia;User=root;Password=RW_qh+-ta5hW*2s" }, "ApiKey": "5929b003-8895-4fb3-bbb0-2eb101c48f66", + "TempAssetsBaseUrl": "https://tools.library.pfw.edu/encyclopedia/pfwencyclopediaassets", + "TempAssetsLocation": "C:\\inetpub\\wwwroot\\pfwencyclopediaassets", "FileAdapterSettings": { // linux or windows - "type": "linux", + "type": "windows", // For Linux - "url": "10.161.100.82", - "username": "serviceuser", - "password": "P!ssword+007", - "basePath": "Applications/PFW Encyclopedia Files" + //"url": "10.161.100.82", + //"username": "serviceuser", + //"password": "P!ssword+007", + //"basePath": "Applications/PFW Encyclopedia Files" // For Windows - //"filePath": "C:\\inetpub\\wwwroot\\PFW Encyclopedia Files\\" + "filePath": "C:\\inetpub\\wwwroot\\PFW Encyclopedia Files\\" } } \ No newline at end of file diff --git a/Library.Encyclopedia.DataAccess/FileAccess/LinuxFileSaveAdapter.cs b/Library.Encyclopedia.DataAccess/FileAccess/LinuxFileSaveAdapter.cs index b1b3222..7eddfd7 100644 --- a/Library.Encyclopedia.DataAccess/FileAccess/LinuxFileSaveAdapter.cs +++ b/Library.Encyclopedia.DataAccess/FileAccess/LinuxFileSaveAdapter.cs @@ -14,19 +14,33 @@ public class LinuxFileSaveAdapter : IFilesAdapter, IDisposable { private readonly string url; private readonly string basePath; + private readonly string tempAssetsFilePath; private readonly NetworkCredential networkCredential; private SftpClient client; - public LinuxFileSaveAdapter(string url, string userName, string password, string basePath) + public LinuxFileSaveAdapter(string url, string userName, string password, string basePath, string tempAssetsFilePath) { this.url = url; this.basePath = basePath; + this.tempAssetsFilePath = tempAssetsFilePath; this.networkCredential = new NetworkCredential(userName, password); client = new SftpClient(url, 22, networkCredential.UserName, networkCredential.Password); client.Connect(); } + public void CreateViewableLink(string id, string name) + { + if (!Directory.Exists(Path.Combine(tempAssetsFilePath, id))) + Directory.CreateDirectory(Path.Combine(tempAssetsFilePath, id)); + + var remotePath = $@"/home/{networkCredential.UserName}/" + basePath + '/' + id.ToString() + '/' + name; + using (Stream fileStream = File.Create(Path.Combine(tempAssetsFilePath, id, name))) + { + client.DownloadFile(remotePath, fileStream); + } + } + public void DeleteFile(string id, string name) { var completeUrl = $@"/home/{networkCredential.UserName}/" + basePath + '/' + id.ToString() + '/' + name; @@ -47,7 +61,7 @@ public void DeleteFiles(string id) { if (file.Name != "." && file.Name != "..") { - client.DeleteFile(file.FullName); + client.DeleteFile(file.FullName); } } @@ -55,6 +69,11 @@ public void DeleteFiles(string id) } } + public void DestroyViewableLink(string id, string name) + { + File.Delete(Path.Combine(tempAssetsFilePath, id, name)); + } + public void Dispose() { this.client.Disconnect(); diff --git a/Library.Encyclopedia.DataAccess/FileAccess/WindowsFileSaveAdapter.cs b/Library.Encyclopedia.DataAccess/FileAccess/WindowsFileSaveAdapter.cs index c2ae715..7ac6be8 100644 --- a/Library.Encyclopedia.DataAccess/FileAccess/WindowsFileSaveAdapter.cs +++ b/Library.Encyclopedia.DataAccess/FileAccess/WindowsFileSaveAdapter.cs @@ -13,10 +13,20 @@ namespace Library.Encyclopedia.DataAccess.FileAccess public class WindowsFileSaveAdapter : IFilesAdapter { private readonly string networkPath; + private readonly string tempAssetFileLocation; - public WindowsFileSaveAdapter(string networkPath) + public WindowsFileSaveAdapter(string networkPath, string tempAssetFileLocation) { this.networkPath = networkPath; + this.tempAssetFileLocation = tempAssetFileLocation; + } + + public void CreateViewableLink(string id, string name) + { + if (!Directory.Exists(Path.Combine(tempAssetFileLocation, id))) + Directory.CreateDirectory(Path.Combine(tempAssetFileLocation, id)); + + File.Copy(Path.Combine(networkPath, id, name), Path.Combine(tempAssetFileLocation, id, name)); } public void DeleteFile(string id, string name) @@ -33,6 +43,11 @@ public void DeleteFiles(string id) Directory.Delete(Path.Combine(networkPath, id)); } + public void DestroyViewableLink(string id, string name) + { + File.Delete(Path.Combine(tempAssetFileLocation, id, name)); + } + public Stream DownloadFile(string id, string name) { return File.OpenRead(Path.Combine(networkPath, id, name)); @@ -59,7 +74,7 @@ public async Task UploadFile(Guid id, IFormFile file) return true; } - catch (Exception ex) + catch (Exception) { return false; } diff --git a/Library.Encyclopedia.Entity/Interfaces/IFilesAdapter.cs b/Library.Encyclopedia.Entity/Interfaces/IFilesAdapter.cs index e65813a..afbc621 100644 --- a/Library.Encyclopedia.Entity/Interfaces/IFilesAdapter.cs +++ b/Library.Encyclopedia.Entity/Interfaces/IFilesAdapter.cs @@ -13,5 +13,8 @@ public interface IFilesAdapter Stream DownloadFile(string id, string name); public void DeleteFiles(string id); public void DeleteFile(string id, string name); + + public void CreateViewableLink(string id, string name); + public void DestroyViewableLink(string id, string name); } } \ No newline at end of file