mirror of
https://git.teknik.io/Teknikode/Teknik.git
synced 2023-08-02 14:16:22 +02:00
Added cache for Upload objects and background processing of download increments
This commit is contained in:
parent
e3dc846b9d
commit
035c927326
@ -262,7 +262,7 @@ namespace Teknik.Areas.Admin.Controllers
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public IActionResult DeleteData(string type, string id)
|
||||
public IActionResult DeleteData(string type, string id, [FromServices] IBackgroundTaskQueue queue)
|
||||
{
|
||||
var context = new ControllerContext();
|
||||
context.HttpContext = Request.HttpContext;
|
||||
@ -272,7 +272,7 @@ namespace Teknik.Areas.Admin.Controllers
|
||||
switch (type)
|
||||
{
|
||||
case "upload":
|
||||
var uploadController = new Upload.Controllers.UploadController(_logger, _config, _dbContext);
|
||||
var uploadController = new Upload.Controllers.UploadController(_logger, _config, _dbContext, queue);
|
||||
uploadController.ControllerContext = context;
|
||||
return uploadController.Delete(id);
|
||||
case "paste":
|
||||
|
@ -32,7 +32,15 @@ namespace Teknik.Areas.Upload.Controllers
|
||||
[Area("Upload")]
|
||||
public class UploadController : DefaultController
|
||||
{
|
||||
public UploadController(ILogger<Logger> logger, Config config, TeknikEntities dbContext) : base(logger, config, dbContext) { }
|
||||
private const int _cacheLength = 300;
|
||||
private readonly ObjectCache _uploadCache;
|
||||
private readonly IBackgroundTaskQueue _queue;
|
||||
|
||||
public UploadController(ILogger<Logger> logger, Config config, TeknikEntities dbContext, IBackgroundTaskQueue queue) : base(logger, config, dbContext)
|
||||
{
|
||||
_uploadCache = new ObjectCache(_cacheLength);
|
||||
_queue = queue;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[AllowAnonymous]
|
||||
@ -225,19 +233,18 @@ namespace Teknik.Areas.Upload.Controllers
|
||||
long contentLength = 0;
|
||||
DateTime dateUploaded = new DateTime();
|
||||
|
||||
Models.Upload upload = _dbContext.Uploads.Where(up => up.Url == file).FirstOrDefault();
|
||||
var upload = UploadHelper.GetUpload(_dbContext, file);
|
||||
if (upload != null)
|
||||
{
|
||||
// Check Expiration
|
||||
if (UploadHelper.CheckExpiration(upload))
|
||||
{
|
||||
UploadHelper.DeleteFile(_dbContext, _config, _logger, upload);
|
||||
UploadHelper.DeleteFile(_dbContext, _config, _logger, file);
|
||||
return new StatusCodeResult(StatusCodes.Status404NotFound);
|
||||
}
|
||||
|
||||
upload.Downloads += 1;
|
||||
_dbContext.Entry(upload).State = EntityState.Modified;
|
||||
_dbContext.SaveChanges();
|
||||
// Increment the download count for this upload
|
||||
UploadHelper.IncrementDownloadCount(_queue, _config, file);
|
||||
|
||||
fileName = upload.FileName;
|
||||
url = upload.Url;
|
||||
@ -431,13 +438,13 @@ namespace Teknik.Areas.Upload.Controllers
|
||||
{
|
||||
if (_config.UploadConfig.DownloadEnabled)
|
||||
{
|
||||
Models.Upload upload = _dbContext.Uploads.Where(up => up.Url == file).FirstOrDefault();
|
||||
Models.Upload upload = UploadHelper.GetUpload(_dbContext, file);
|
||||
if (upload != null)
|
||||
{
|
||||
// Check Expiration
|
||||
if (UploadHelper.CheckExpiration(upload))
|
||||
{
|
||||
UploadHelper.DeleteFile(_dbContext, _config, _logger, upload);
|
||||
UploadHelper.DeleteFile(_dbContext, _config, _logger, file);
|
||||
return Json(new { error = new { message = "File Does Not Exist" } });
|
||||
}
|
||||
|
||||
@ -485,14 +492,14 @@ namespace Teknik.Areas.Upload.Controllers
|
||||
public IActionResult DeleteByKey(string file, string key)
|
||||
{
|
||||
ViewBag.Title = "File Delete | " + file ;
|
||||
Models.Upload upload = _dbContext.Uploads.Where(up => up.Url == file).FirstOrDefault();
|
||||
Models.Upload upload = UploadHelper.GetUpload(_dbContext, file);
|
||||
if (upload != null)
|
||||
{
|
||||
DeleteViewModel model = new DeleteViewModel();
|
||||
model.File = file;
|
||||
if (!string.IsNullOrEmpty(upload.DeleteKey) && upload.DeleteKey == key)
|
||||
{
|
||||
UploadHelper.DeleteFile(_dbContext, _config, _logger, upload);
|
||||
UploadHelper.DeleteFile(_dbContext, _config, _logger, file);
|
||||
model.Deleted = true;
|
||||
}
|
||||
else
|
||||
@ -507,16 +514,13 @@ namespace Teknik.Areas.Upload.Controllers
|
||||
[HttpPost]
|
||||
public IActionResult GenerateDeleteKey(string file)
|
||||
{
|
||||
Models.Upload upload = _dbContext.Uploads.Where(up => up.Url == file).FirstOrDefault();
|
||||
Models.Upload upload = UploadHelper.GetUpload(_dbContext, file);
|
||||
if (upload != null)
|
||||
{
|
||||
if (upload.User?.Username == User.Identity.Name ||
|
||||
User.IsInRole("Admin"))
|
||||
{
|
||||
string delKey = StringHelper.RandomString(_config.UploadConfig.DeleteKeyLength);
|
||||
upload.DeleteKey = delKey;
|
||||
_dbContext.Entry(upload).State = EntityState.Modified;
|
||||
_dbContext.SaveChanges();
|
||||
var delKey = UploadHelper.GenerateDeleteKey(_dbContext, _config, file);
|
||||
return Json(new { result = new { url = Url.SubRouteUrl("u", "Upload.DeleteByKey", new { file = file, key = delKey }) } });
|
||||
}
|
||||
return Json(new { error = new { message = "You do not have permission to delete this Upload" } });
|
||||
@ -534,7 +538,7 @@ namespace Teknik.Areas.Upload.Controllers
|
||||
if (foundUpload.User?.Username == User.Identity.Name ||
|
||||
User.IsInRole("Admin"))
|
||||
{
|
||||
UploadHelper.DeleteFile(_dbContext, _config, _logger, foundUpload);
|
||||
UploadHelper.DeleteFile(_dbContext, _config, _logger, id);
|
||||
return Json(new { result = true });
|
||||
}
|
||||
return Json(new { error = new { message = "You do not have permission to delete this Upload" } });
|
||||
|
@ -12,11 +12,15 @@ using Teknik.Data;
|
||||
using Teknik.StorageService;
|
||||
using Teknik.Logging;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Teknik.Areas.Upload
|
||||
{
|
||||
public static class UploadHelper
|
||||
{
|
||||
private static object _cacheLock = new object();
|
||||
private readonly static ObjectCache _uploadCache = new ObjectCache(300);
|
||||
|
||||
public static Models.Upload SaveFile(TeknikEntities db, Config config, Stream file, string contentType, long contentLength, bool encrypt, ExpirationUnit expirationUnit, int expirationLength)
|
||||
{
|
||||
return SaveFile(db, config, file, contentType, contentLength, encrypt, expirationUnit, expirationLength, string.Empty, null, null, 256, 128);
|
||||
@ -119,6 +123,19 @@ namespace Teknik.Areas.Upload
|
||||
return upload;
|
||||
}
|
||||
|
||||
public static string GenerateDeleteKey(TeknikEntities db, Config config, string url)
|
||||
{
|
||||
var upload = db.Uploads.FirstOrDefault(up => up.Url == url);
|
||||
if (upload != null)
|
||||
{
|
||||
string delKey = StringHelper.RandomString(config.UploadConfig.DeleteKeyLength);
|
||||
upload.DeleteKey = delKey;
|
||||
ModifyUpload(db, upload);
|
||||
return delKey;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool CheckExpiration(Models.Upload upload)
|
||||
{
|
||||
if (upload.ExpireDate != null && DateTime.Now >= upload.ExpireDate)
|
||||
@ -129,15 +146,37 @@ namespace Teknik.Areas.Upload
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Models.Upload GetUpload(TeknikEntities db, string url)
|
||||
public static void IncrementDownloadCount(IBackgroundTaskQueue queue, Config config, string url)
|
||||
{
|
||||
Models.Upload upload = db.Uploads.Where(up => up.Url == url).FirstOrDefault();
|
||||
// Fire and forget updating of the download count
|
||||
queue.QueueBackgroundWorkItem(async token =>
|
||||
{
|
||||
var optionsBuilder = new DbContextOptionsBuilder<TeknikEntities>();
|
||||
optionsBuilder.UseSqlServer(config.DbConnection);
|
||||
|
||||
return upload;
|
||||
using (TeknikEntities db = new TeknikEntities(optionsBuilder.Options))
|
||||
{
|
||||
var upload = db.Uploads.FirstOrDefault(up => up.Url == url);
|
||||
if (upload != null)
|
||||
{
|
||||
upload.Downloads++;
|
||||
ModifyUpload(db, upload);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void DeleteFile(TeknikEntities db, Config config, ILogger<Logger> logger, Models.Upload upload)
|
||||
public static Models.Upload GetUpload(TeknikEntities db, string url)
|
||||
{
|
||||
lock (_cacheLock)
|
||||
{
|
||||
return _uploadCache.GetObject(url, (key) => db.Uploads.FirstOrDefault(up => up.Url == key));
|
||||
}
|
||||
}
|
||||
|
||||
public static void DeleteFile(TeknikEntities db, Config config, ILogger<Logger> logger, string url)
|
||||
{
|
||||
var upload = db.Uploads.FirstOrDefault(up => up.Url == url);
|
||||
try
|
||||
{
|
||||
var storageService = StorageServiceFactory.GetStorageService(config.UploadConfig.StorageConfig);
|
||||
@ -148,9 +187,28 @@ namespace Teknik.Areas.Upload
|
||||
logger.LogError(ex, "Unable to delete file: {0}", upload.FileName);
|
||||
}
|
||||
|
||||
// Remove from the cache
|
||||
lock (_cacheLock)
|
||||
{
|
||||
_uploadCache.DeleteObject(upload.FileName);
|
||||
}
|
||||
|
||||
// Delete from the DB
|
||||
db.Uploads.Remove(upload);
|
||||
db.SaveChanges();
|
||||
}
|
||||
|
||||
public static void ModifyUpload(TeknikEntities db, Models.Upload upload)
|
||||
{
|
||||
// Update the cache's copy
|
||||
lock (_cacheLock)
|
||||
{
|
||||
_uploadCache.UpdateObject(upload.Url, upload);
|
||||
}
|
||||
|
||||
// Update the database
|
||||
db.Entry(upload).State = EntityState.Modified;
|
||||
db.SaveChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1421,7 +1421,7 @@ namespace Teknik.Areas.Users.Controllers
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public IActionResult DeleteData(string type, string id)
|
||||
public IActionResult DeleteData(string type, string id, [FromServices] IBackgroundTaskQueue queue)
|
||||
{
|
||||
var context = new ControllerContext();
|
||||
context.HttpContext = Request.HttpContext;
|
||||
@ -1431,7 +1431,7 @@ namespace Teknik.Areas.Users.Controllers
|
||||
switch (type)
|
||||
{
|
||||
case "upload":
|
||||
var uploadController = new Upload.Controllers.UploadController(_logger, _config, _dbContext);
|
||||
var uploadController = new Upload.Controllers.UploadController(_logger, _config, _dbContext, queue);
|
||||
uploadController.ControllerContext = context;
|
||||
return uploadController.Delete(id);
|
||||
case "paste":
|
||||
|
57
Utilities/ObjectCache.cs
Normal file
57
Utilities/ObjectCache.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Teknik.Utilities
|
||||
{
|
||||
public class ObjectCache
|
||||
{
|
||||
private readonly static Dictionary<string, Tuple<DateTime, object>> objectCache = new Dictionary<string, Tuple<DateTime, object>>();
|
||||
private readonly int _cacheSeconds;
|
||||
|
||||
public ObjectCache(int cacheSeconds)
|
||||
{
|
||||
_cacheSeconds = cacheSeconds;
|
||||
}
|
||||
|
||||
public T GetObject<T>(string key, Func<string, T> getObjectFunc)
|
||||
{
|
||||
T foundObject;
|
||||
var cacheDate = DateTime.UtcNow;
|
||||
if (objectCache.TryGetValue(key, out var result) &&
|
||||
result.Item1 > cacheDate.Subtract(new TimeSpan(0, 0, _cacheSeconds)))
|
||||
{
|
||||
cacheDate = result.Item1;
|
||||
foundObject = (T)result.Item2;
|
||||
}
|
||||
else
|
||||
{
|
||||
foundObject = getObjectFunc(key);
|
||||
}
|
||||
|
||||
if (foundObject != null)
|
||||
objectCache[key] = new Tuple<DateTime, object>(cacheDate, foundObject);
|
||||
|
||||
return foundObject;
|
||||
}
|
||||
|
||||
public void UpdateObject<T>(string key, T update)
|
||||
{
|
||||
var cacheDate = DateTime.UtcNow;
|
||||
if (objectCache.TryGetValue(key, out var result))
|
||||
{
|
||||
if (result.Item1 <= cacheDate.Subtract(new TimeSpan(0, 0, _cacheSeconds)))
|
||||
DeleteObject(key);
|
||||
else
|
||||
objectCache[key] = new Tuple<DateTime, object>(result.Item1, update);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteObject(string key)
|
||||
{
|
||||
objectCache.Remove(key);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user