mirror of
https://git.teknik.io/Teknikode/Teknik.git
synced 2023-08-02 14:16:22 +02:00
Fixed caching to be singleton w/ background cleaner
This commit is contained in:
parent
ad532b0106
commit
0007dc8690
@ -261,7 +261,7 @@ namespace Teknik.Areas.Admin.Controllers
|
|||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[ValidateAntiForgeryToken]
|
[ValidateAntiForgeryToken]
|
||||||
public IActionResult DeleteData(string type, string id, [FromServices] IBackgroundTaskQueue queue)
|
public IActionResult DeleteData(string type, string id, [FromServices] IBackgroundTaskQueue queue, [FromServices] ObjectCache cache)
|
||||||
{
|
{
|
||||||
var context = new ControllerContext();
|
var context = new ControllerContext();
|
||||||
context.HttpContext = Request.HttpContext;
|
context.HttpContext = Request.HttpContext;
|
||||||
@ -271,11 +271,11 @@ namespace Teknik.Areas.Admin.Controllers
|
|||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case "upload":
|
case "upload":
|
||||||
var uploadController = new Upload.Controllers.UploadController(_logger, _config, _dbContext, queue);
|
var uploadController = new Upload.Controllers.UploadController(_logger, _config, _dbContext, queue, cache);
|
||||||
uploadController.ControllerContext = context;
|
uploadController.ControllerContext = context;
|
||||||
return uploadController.Delete(id);
|
return uploadController.Delete(id);
|
||||||
case "paste":
|
case "paste":
|
||||||
var pasteController = new Paste.Controllers.PasteController(_logger, _config, _dbContext, queue);
|
var pasteController = new Paste.Controllers.PasteController(_logger, _config, _dbContext, queue, cache);
|
||||||
pasteController.ControllerContext = context;
|
pasteController.ControllerContext = context;
|
||||||
return pasteController.Delete(id);
|
return pasteController.Delete(id);
|
||||||
case "shortenedUrl":
|
case "shortenedUrl":
|
||||||
|
@ -26,10 +26,12 @@ namespace Teknik.Areas.Paste.Controllers
|
|||||||
public class PasteController : DefaultController
|
public class PasteController : DefaultController
|
||||||
{
|
{
|
||||||
private readonly IBackgroundTaskQueue _queue;
|
private readonly IBackgroundTaskQueue _queue;
|
||||||
|
private readonly ObjectCache _cache;
|
||||||
|
|
||||||
public PasteController(ILogger<Logger> logger, Config config, TeknikEntities dbContext, IBackgroundTaskQueue queue) : base(logger, config, dbContext)
|
public PasteController(ILogger<Logger> logger, Config config, TeknikEntities dbContext, IBackgroundTaskQueue queue, ObjectCache cache) : base(logger, config, dbContext)
|
||||||
{
|
{
|
||||||
_queue = queue;
|
_queue = queue;
|
||||||
|
_cache = cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
@ -46,7 +48,7 @@ namespace Teknik.Areas.Paste.Controllers
|
|||||||
[TrackPageView]
|
[TrackPageView]
|
||||||
public async Task<IActionResult> ViewPaste(string type, string url, string password)
|
public async Task<IActionResult> ViewPaste(string type, string url, string password)
|
||||||
{
|
{
|
||||||
Models.Paste paste = PasteHelper.GetPaste(_dbContext, url);
|
Models.Paste paste = PasteHelper.GetPaste(_dbContext, _cache, url);
|
||||||
if (paste != null)
|
if (paste != null)
|
||||||
{
|
{
|
||||||
ViewBag.Title = (string.IsNullOrEmpty(paste.Title)) ? "Untitled Paste" : paste.Title + " | Pastebin";
|
ViewBag.Title = (string.IsNullOrEmpty(paste.Title)) ? "Untitled Paste" : paste.Title + " | Pastebin";
|
||||||
@ -62,12 +64,12 @@ namespace Teknik.Areas.Paste.Controllers
|
|||||||
// Check Expiration
|
// Check Expiration
|
||||||
if (PasteHelper.CheckExpiration(paste))
|
if (PasteHelper.CheckExpiration(paste))
|
||||||
{
|
{
|
||||||
PasteHelper.DeleteFile(_dbContext, _config, _logger, url);
|
PasteHelper.DeleteFile(_dbContext, _cache, _config, _logger, url);
|
||||||
return new StatusCodeResult(StatusCodes.Status404NotFound);
|
return new StatusCodeResult(StatusCodes.Status404NotFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Increment View Count
|
// Increment View Count
|
||||||
PasteHelper.IncrementViewCount(_queue, _config, url);
|
PasteHelper.IncrementViewCount(_queue, _cache, _config, url);
|
||||||
|
|
||||||
PasteViewModel model = new PasteViewModel();
|
PasteViewModel model = new PasteViewModel();
|
||||||
model.Url = url;
|
model.Url = url;
|
||||||
@ -234,7 +236,7 @@ namespace Teknik.Areas.Paste.Controllers
|
|||||||
[TrackPageView]
|
[TrackPageView]
|
||||||
public async Task<IActionResult> Edit(string url, string password)
|
public async Task<IActionResult> Edit(string url, string password)
|
||||||
{
|
{
|
||||||
Models.Paste paste = PasteHelper.GetPaste(_dbContext, url);
|
Models.Paste paste = PasteHelper.GetPaste(_dbContext, _cache, url);
|
||||||
if (paste != null)
|
if (paste != null)
|
||||||
{
|
{
|
||||||
if (paste.User?.Username != User.Identity.Name)
|
if (paste.User?.Username != User.Identity.Name)
|
||||||
@ -246,7 +248,7 @@ namespace Teknik.Areas.Paste.Controllers
|
|||||||
// Check Expiration
|
// Check Expiration
|
||||||
if (PasteHelper.CheckExpiration(paste))
|
if (PasteHelper.CheckExpiration(paste))
|
||||||
{
|
{
|
||||||
PasteHelper.DeleteFile(_dbContext, _config, _logger, url);
|
PasteHelper.DeleteFile(_dbContext, _cache, _config, _logger, url);
|
||||||
return new StatusCodeResult(StatusCodes.Status404NotFound);
|
return new StatusCodeResult(StatusCodes.Status404NotFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,7 +340,7 @@ namespace Teknik.Areas.Paste.Controllers
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Models.Paste paste = PasteHelper.GetPaste(_dbContext, model.Url);
|
Models.Paste paste = PasteHelper.GetPaste(_dbContext, _cache, model.Url);
|
||||||
if (paste != null)
|
if (paste != null)
|
||||||
{
|
{
|
||||||
if (paste.User?.Username != User.Identity.Name)
|
if (paste.User?.Username != User.Identity.Name)
|
||||||
@ -396,7 +398,7 @@ namespace Teknik.Areas.Paste.Controllers
|
|||||||
paste.Syntax = model.Syntax;
|
paste.Syntax = model.Syntax;
|
||||||
paste.DateEdited = DateTime.Now;
|
paste.DateEdited = DateTime.Now;
|
||||||
|
|
||||||
PasteHelper.ModifyPaste(_dbContext, paste);
|
PasteHelper.ModifyPaste(_dbContext, _cache, paste);
|
||||||
|
|
||||||
// Delete the old file
|
// Delete the old file
|
||||||
storageService.DeleteFile(oldFile);
|
storageService.DeleteFile(oldFile);
|
||||||
@ -416,13 +418,13 @@ namespace Teknik.Areas.Paste.Controllers
|
|||||||
[HttpOptions]
|
[HttpOptions]
|
||||||
public IActionResult Delete(string id)
|
public IActionResult Delete(string id)
|
||||||
{
|
{
|
||||||
Models.Paste foundPaste = PasteHelper.GetPaste(_dbContext, id);
|
Models.Paste foundPaste = PasteHelper.GetPaste(_dbContext, _cache, id);
|
||||||
if (foundPaste != null)
|
if (foundPaste != null)
|
||||||
{
|
{
|
||||||
if (foundPaste.User?.Username == User.Identity.Name ||
|
if (foundPaste.User?.Username == User.Identity.Name ||
|
||||||
User.IsInRole("Admin"))
|
User.IsInRole("Admin"))
|
||||||
{
|
{
|
||||||
PasteHelper.DeleteFile(_dbContext, _config, _logger, id);
|
PasteHelper.DeleteFile(_dbContext, _cache, _config, _logger, id);
|
||||||
|
|
||||||
return Json(new { result = true, redirect = Url.SubRouteUrl("p", "Paste.Index") });
|
return Json(new { result = true, redirect = Url.SubRouteUrl("p", "Paste.Index") });
|
||||||
}
|
}
|
||||||
|
@ -19,9 +19,6 @@ namespace Teknik.Areas.Paste
|
|||||||
{
|
{
|
||||||
public static class PasteHelper
|
public static class PasteHelper
|
||||||
{
|
{
|
||||||
private static object _cacheLock = new object();
|
|
||||||
private readonly static ObjectCache _pasteCache = new ObjectCache(300);
|
|
||||||
|
|
||||||
public static async Task<Models.Paste> CreatePaste(Config config, TeknikEntities db, string content, string title = "", string syntax = "text", ExpirationUnit expireUnit = ExpirationUnit.Never, int expireLength = 1, string password = "")
|
public static async Task<Models.Paste> CreatePaste(Config config, TeknikEntities db, string content, string title = "", string syntax = "text", ExpirationUnit expireUnit = ExpirationUnit.Never, int expireLength = 1, string password = "")
|
||||||
{
|
{
|
||||||
Models.Paste paste = new Models.Paste();
|
Models.Paste paste = new Models.Paste();
|
||||||
@ -127,7 +124,7 @@ namespace Teknik.Areas.Paste
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void IncrementViewCount(IBackgroundTaskQueue queue, Config config, string url)
|
public static void IncrementViewCount(IBackgroundTaskQueue queue, ObjectCache cache, Config config, string url)
|
||||||
{
|
{
|
||||||
// Fire and forget updating of the download count
|
// Fire and forget updating of the download count
|
||||||
queue.QueueBackgroundWorkItem(async token =>
|
queue.QueueBackgroundWorkItem(async token =>
|
||||||
@ -139,22 +136,20 @@ namespace Teknik.Areas.Paste
|
|||||||
|
|
||||||
using (TeknikEntities db = new TeknikEntities(optionsBuilder.Options))
|
using (TeknikEntities db = new TeknikEntities(optionsBuilder.Options))
|
||||||
{
|
{
|
||||||
var paste = GetPaste(db, url);
|
var paste = GetPaste(db, cache, url);
|
||||||
if (paste != null)
|
if (paste != null)
|
||||||
{
|
{
|
||||||
paste.Views++;
|
paste.Views++;
|
||||||
ModifyPaste(db, paste);
|
ModifyPaste(db, cache, paste);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Models.Paste GetPaste(TeknikEntities db, string url)
|
public static Models.Paste GetPaste(TeknikEntities db, ObjectCache cache, string url)
|
||||||
{
|
{
|
||||||
lock (_cacheLock)
|
var paste = cache.AddOrGetObject(url, (key) => db.Pastes.FirstOrDefault(up => up.Url == key));
|
||||||
{
|
|
||||||
var paste = _pasteCache.GetObject(url, (key) => db.Pastes.FirstOrDefault(up => up.Url == key));
|
|
||||||
|
|
||||||
if (paste != null &&
|
if (paste != null &&
|
||||||
!db.Exists(paste))
|
!db.Exists(paste))
|
||||||
@ -162,11 +157,10 @@ namespace Teknik.Areas.Paste
|
|||||||
|
|
||||||
return paste;
|
return paste;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static void DeleteFile(TeknikEntities db, Config config, ILogger<Logger> logger, string url)
|
public static void DeleteFile(TeknikEntities db, ObjectCache cache, Config config, ILogger<Logger> logger, string url)
|
||||||
{
|
{
|
||||||
var paste = GetPaste(db, url);
|
var paste = GetPaste(db, cache, url);
|
||||||
if (paste != null)
|
if (paste != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -185,19 +179,13 @@ namespace Teknik.Areas.Paste
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove from the cache
|
// Remove from the cache
|
||||||
lock (_cacheLock)
|
cache.DeleteObject<PasteConfig>(url);
|
||||||
{
|
|
||||||
_pasteCache.DeleteObject(url);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ModifyPaste(TeknikEntities db, Models.Paste paste)
|
public static void ModifyPaste(TeknikEntities db, ObjectCache cache, Models.Paste paste)
|
||||||
{
|
{
|
||||||
// Update the cache's copy
|
// Update the cache's copy
|
||||||
lock (_cacheLock)
|
cache.UpdateObject(paste.Url, paste);
|
||||||
{
|
|
||||||
_pasteCache.UpdateObject(paste.Url, paste);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (paste != null)
|
if (paste != null)
|
||||||
{
|
{
|
||||||
|
@ -29,10 +29,12 @@ namespace Teknik.Areas.Upload.Controllers
|
|||||||
public class UploadController : DefaultController
|
public class UploadController : DefaultController
|
||||||
{
|
{
|
||||||
private readonly IBackgroundTaskQueue _queue;
|
private readonly IBackgroundTaskQueue _queue;
|
||||||
|
private readonly ObjectCache _cache;
|
||||||
|
|
||||||
public UploadController(ILogger<Logger> logger, Config config, TeknikEntities dbContext, IBackgroundTaskQueue queue) : base(logger, config, dbContext)
|
public UploadController(ILogger<Logger> logger, Config config, TeknikEntities dbContext, IBackgroundTaskQueue queue, ObjectCache cache) : base(logger, config, dbContext)
|
||||||
{
|
{
|
||||||
_queue = queue;
|
_queue = queue;
|
||||||
|
_cache = cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
@ -226,18 +228,18 @@ namespace Teknik.Areas.Upload.Controllers
|
|||||||
long contentLength = 0;
|
long contentLength = 0;
|
||||||
DateTime dateUploaded = new DateTime();
|
DateTime dateUploaded = new DateTime();
|
||||||
|
|
||||||
var upload = UploadHelper.GetUpload(_dbContext, file);
|
var upload = UploadHelper.GetUpload(_dbContext, _cache, file);
|
||||||
if (upload != null)
|
if (upload != null)
|
||||||
{
|
{
|
||||||
// Check Expiration
|
// Check Expiration
|
||||||
if (UploadHelper.CheckExpiration(upload))
|
if (UploadHelper.CheckExpiration(upload))
|
||||||
{
|
{
|
||||||
UploadHelper.DeleteFile(_dbContext, _config, _logger, file);
|
UploadHelper.DeleteFile(_dbContext, _cache, _config, _logger, file);
|
||||||
return new StatusCodeResult(StatusCodes.Status404NotFound);
|
return new StatusCodeResult(StatusCodes.Status404NotFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Increment the download count for this upload
|
// Increment the download count for this upload
|
||||||
UploadHelper.IncrementDownloadCount(_queue, _config, file);
|
UploadHelper.IncrementDownloadCount(_queue, _cache, _config, file);
|
||||||
|
|
||||||
fileName = upload.FileName;
|
fileName = upload.FileName;
|
||||||
url = upload.Url;
|
url = upload.Url;
|
||||||
@ -412,13 +414,13 @@ namespace Teknik.Areas.Upload.Controllers
|
|||||||
{
|
{
|
||||||
if (_config.UploadConfig.DownloadEnabled)
|
if (_config.UploadConfig.DownloadEnabled)
|
||||||
{
|
{
|
||||||
Models.Upload upload = UploadHelper.GetUpload(_dbContext, file);
|
Models.Upload upload = UploadHelper.GetUpload(_dbContext, _cache, file);
|
||||||
if (upload != null)
|
if (upload != null)
|
||||||
{
|
{
|
||||||
// Check Expiration
|
// Check Expiration
|
||||||
if (UploadHelper.CheckExpiration(upload))
|
if (UploadHelper.CheckExpiration(upload))
|
||||||
{
|
{
|
||||||
UploadHelper.DeleteFile(_dbContext, _config, _logger, file);
|
UploadHelper.DeleteFile(_dbContext, _cache, _config, _logger, file);
|
||||||
return Json(new { error = new { message = "File Does Not Exist" } });
|
return Json(new { error = new { message = "File Does Not Exist" } });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -454,14 +456,14 @@ namespace Teknik.Areas.Upload.Controllers
|
|||||||
public IActionResult DeleteByKey(string file, string key)
|
public IActionResult DeleteByKey(string file, string key)
|
||||||
{
|
{
|
||||||
ViewBag.Title = "File Delete | " + file ;
|
ViewBag.Title = "File Delete | " + file ;
|
||||||
Models.Upload upload = UploadHelper.GetUpload(_dbContext, file);
|
Models.Upload upload = UploadHelper.GetUpload(_dbContext, _cache, file);
|
||||||
if (upload != null)
|
if (upload != null)
|
||||||
{
|
{
|
||||||
DeleteViewModel model = new DeleteViewModel();
|
DeleteViewModel model = new DeleteViewModel();
|
||||||
model.File = file;
|
model.File = file;
|
||||||
if (!string.IsNullOrEmpty(upload.DeleteKey) && upload.DeleteKey == key)
|
if (!string.IsNullOrEmpty(upload.DeleteKey) && upload.DeleteKey == key)
|
||||||
{
|
{
|
||||||
UploadHelper.DeleteFile(_dbContext, _config, _logger, file);
|
UploadHelper.DeleteFile(_dbContext, _cache, _config, _logger, file);
|
||||||
model.Deleted = true;
|
model.Deleted = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -476,13 +478,13 @@ namespace Teknik.Areas.Upload.Controllers
|
|||||||
[HttpPost]
|
[HttpPost]
|
||||||
public IActionResult GenerateDeleteKey(string file)
|
public IActionResult GenerateDeleteKey(string file)
|
||||||
{
|
{
|
||||||
Models.Upload upload = UploadHelper.GetUpload(_dbContext, file);
|
Models.Upload upload = UploadHelper.GetUpload(_dbContext, _cache, file);
|
||||||
if (upload != null)
|
if (upload != null)
|
||||||
{
|
{
|
||||||
if (upload.User?.Username == User.Identity.Name ||
|
if (upload.User?.Username == User.Identity.Name ||
|
||||||
User.IsInRole("Admin"))
|
User.IsInRole("Admin"))
|
||||||
{
|
{
|
||||||
var delKey = UploadHelper.GenerateDeleteKey(_dbContext, _config, file);
|
var delKey = UploadHelper.GenerateDeleteKey(_dbContext, _cache, _config, file);
|
||||||
return Json(new { result = new { url = Url.SubRouteUrl("u", "Upload.DeleteByKey", new { file = file, key = delKey }) } });
|
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" } });
|
return Json(new { error = new { message = "You do not have permission to delete this Upload" } });
|
||||||
@ -494,13 +496,13 @@ namespace Teknik.Areas.Upload.Controllers
|
|||||||
[HttpOptions]
|
[HttpOptions]
|
||||||
public IActionResult Delete(string id)
|
public IActionResult Delete(string id)
|
||||||
{
|
{
|
||||||
Models.Upload foundUpload = UploadHelper.GetUpload(_dbContext, id);
|
Models.Upload foundUpload = UploadHelper.GetUpload(_dbContext, _cache, id);
|
||||||
if (foundUpload != null)
|
if (foundUpload != null)
|
||||||
{
|
{
|
||||||
if (foundUpload.User?.Username == User.Identity.Name ||
|
if (foundUpload.User?.Username == User.Identity.Name ||
|
||||||
User.IsInRole("Admin"))
|
User.IsInRole("Admin"))
|
||||||
{
|
{
|
||||||
UploadHelper.DeleteFile(_dbContext, _config, _logger, id);
|
UploadHelper.DeleteFile(_dbContext, _cache, _config, _logger, id);
|
||||||
return Json(new { result = true });
|
return Json(new { result = true });
|
||||||
}
|
}
|
||||||
return Json(new { error = new { message = "You do not have permission to delete this Upload" } });
|
return Json(new { error = new { message = "You do not have permission to delete this Upload" } });
|
||||||
|
@ -19,9 +19,6 @@ namespace Teknik.Areas.Upload
|
|||||||
{
|
{
|
||||||
public static class UploadHelper
|
public static class UploadHelper
|
||||||
{
|
{
|
||||||
private static object _cacheLock = new object();
|
|
||||||
private readonly static ObjectCache _uploadCache = new ObjectCache(300);
|
|
||||||
|
|
||||||
public static async Task<Models.Upload> SaveFile(TeknikEntities db, Config config, Stream file, string contentType, long contentLength, bool encrypt, ExpirationUnit expirationUnit, int expirationLength)
|
public static async Task<Models.Upload> SaveFile(TeknikEntities db, Config config, Stream file, string contentType, long contentLength, bool encrypt, ExpirationUnit expirationUnit, int expirationLength)
|
||||||
{
|
{
|
||||||
return await SaveFile(db, config, file, contentType, contentLength, encrypt, expirationUnit, expirationLength, string.Empty, null, null, 256, 128);
|
return await SaveFile(db, config, file, contentType, contentLength, encrypt, expirationUnit, expirationLength, string.Empty, null, null, 256, 128);
|
||||||
@ -128,14 +125,14 @@ namespace Teknik.Areas.Upload
|
|||||||
return upload;
|
return upload;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GenerateDeleteKey(TeknikEntities db, Config config, string url)
|
public static string GenerateDeleteKey(TeknikEntities db, ObjectCache cache, Config config, string url)
|
||||||
{
|
{
|
||||||
var upload = db.Uploads.FirstOrDefault(up => up.Url == url);
|
var upload = db.Uploads.FirstOrDefault(up => up.Url == url);
|
||||||
if (upload != null)
|
if (upload != null)
|
||||||
{
|
{
|
||||||
string delKey = StringHelper.RandomString(config.UploadConfig.DeleteKeyLength);
|
string delKey = StringHelper.RandomString(config.UploadConfig.DeleteKeyLength);
|
||||||
upload.DeleteKey = delKey;
|
upload.DeleteKey = delKey;
|
||||||
ModifyUpload(db, upload);
|
ModifyUpload(db, cache, upload);
|
||||||
return delKey;
|
return delKey;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@ -151,7 +148,7 @@ namespace Teknik.Areas.Upload
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void IncrementDownloadCount(IBackgroundTaskQueue queue, Config config, string url)
|
public static void IncrementDownloadCount(IBackgroundTaskQueue queue, ObjectCache cache, Config config, string url)
|
||||||
{
|
{
|
||||||
// Fire and forget updating of the download count
|
// Fire and forget updating of the download count
|
||||||
queue.QueueBackgroundWorkItem(async token =>
|
queue.QueueBackgroundWorkItem(async token =>
|
||||||
@ -163,22 +160,20 @@ namespace Teknik.Areas.Upload
|
|||||||
|
|
||||||
using (TeknikEntities db = new TeknikEntities(optionsBuilder.Options))
|
using (TeknikEntities db = new TeknikEntities(optionsBuilder.Options))
|
||||||
{
|
{
|
||||||
var upload = GetUpload(db, url);
|
var upload = GetUpload(db, cache, url);
|
||||||
if (upload != null)
|
if (upload != null)
|
||||||
{
|
{
|
||||||
upload.Downloads++;
|
upload.Downloads++;
|
||||||
ModifyUpload(db, upload);
|
ModifyUpload(db, cache, upload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Models.Upload GetUpload(TeknikEntities db, string url)
|
public static Models.Upload GetUpload(TeknikEntities db, ObjectCache cache, string url)
|
||||||
{
|
{
|
||||||
lock (_cacheLock)
|
var upload = cache.AddOrGetObject(url, (key) => db.Uploads.FirstOrDefault(up => up.Url == key));
|
||||||
{
|
|
||||||
var upload = _uploadCache.GetObject(url, (key) => db.Uploads.FirstOrDefault(up => up.Url == key));
|
|
||||||
|
|
||||||
if (upload != null &&
|
if (upload != null &&
|
||||||
!db.Exists(upload))
|
!db.Exists(upload))
|
||||||
@ -186,11 +181,10 @@ namespace Teknik.Areas.Upload
|
|||||||
|
|
||||||
return upload;
|
return upload;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static void DeleteFile(TeknikEntities db, Config config, ILogger<Logger> logger, string url)
|
public static void DeleteFile(TeknikEntities db, ObjectCache cache, Config config, ILogger<Logger> logger, string url)
|
||||||
{
|
{
|
||||||
var upload = GetUpload(db, url);
|
var upload = GetUpload(db, cache, url);
|
||||||
if (upload != null)
|
if (upload != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -209,19 +203,13 @@ namespace Teknik.Areas.Upload
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove from the cache
|
// Remove from the cache
|
||||||
lock (_cacheLock)
|
cache.DeleteObject<UploadConfig>(url);
|
||||||
{
|
|
||||||
_uploadCache.DeleteObject(url);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ModifyUpload(TeknikEntities db, Models.Upload upload)
|
public static void ModifyUpload(TeknikEntities db, ObjectCache cache, Models.Upload upload)
|
||||||
{
|
{
|
||||||
// Update the cache's copy
|
// Update the cache's copy
|
||||||
lock (_cacheLock)
|
cache.UpdateObject(upload.Url, upload);
|
||||||
{
|
|
||||||
_uploadCache.UpdateObject(upload.Url, upload);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (upload != null)
|
if (upload != null)
|
||||||
{
|
{
|
||||||
|
@ -1408,7 +1408,7 @@ namespace Teknik.Areas.Users.Controllers
|
|||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[ValidateAntiForgeryToken]
|
[ValidateAntiForgeryToken]
|
||||||
public IActionResult DeleteData(string type, string id, [FromServices] IBackgroundTaskQueue queue)
|
public IActionResult DeleteData(string type, string id, [FromServices] IBackgroundTaskQueue queue, [FromServices] ObjectCache cache)
|
||||||
{
|
{
|
||||||
var context = new ControllerContext();
|
var context = new ControllerContext();
|
||||||
context.HttpContext = Request.HttpContext;
|
context.HttpContext = Request.HttpContext;
|
||||||
@ -1418,11 +1418,11 @@ namespace Teknik.Areas.Users.Controllers
|
|||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case "upload":
|
case "upload":
|
||||||
var uploadController = new Upload.Controllers.UploadController(_logger, _config, _dbContext, queue);
|
var uploadController = new Upload.Controllers.UploadController(_logger, _config, _dbContext, queue, cache);
|
||||||
uploadController.ControllerContext = context;
|
uploadController.ControllerContext = context;
|
||||||
return uploadController.Delete(id);
|
return uploadController.Delete(id);
|
||||||
case "paste":
|
case "paste":
|
||||||
var pasteController = new Paste.Controllers.PasteController(_logger, _config, _dbContext, queue);
|
var pasteController = new Paste.Controllers.PasteController(_logger, _config, _dbContext, queue, cache);
|
||||||
pasteController.ControllerContext = context;
|
pasteController.ControllerContext = context;
|
||||||
return pasteController.Delete(id);
|
return pasteController.Delete(id);
|
||||||
case "shortenedUrl":
|
case "shortenedUrl":
|
||||||
|
43
Teknik/Services/CacheCleanerService.cs
Normal file
43
Teknik/Services/CacheCleanerService.cs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Teknik.Logging;
|
||||||
|
using Teknik.Utilities;
|
||||||
|
|
||||||
|
namespace Teknik.Services
|
||||||
|
{
|
||||||
|
public class CacheCleanerService : BackgroundService
|
||||||
|
{
|
||||||
|
private readonly ILogger<Logger> _logger;
|
||||||
|
public readonly ObjectCache _cache;
|
||||||
|
|
||||||
|
public CacheCleanerService(ObjectCache cache, ILogger<Logger> logger)
|
||||||
|
{
|
||||||
|
_cache = cache;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async override Task ExecuteAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Cache Cleaning Service is starting.");
|
||||||
|
|
||||||
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_cache.CleanCache();
|
||||||
|
|
||||||
|
await Task.Delay(new TimeSpan(0, 5, 0), cancellationToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, $"Error occurred cleaning cache.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("Cache Cleaning Service is stopping.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -101,6 +101,8 @@ namespace Teknik
|
|||||||
|
|
||||||
services.AddHostedService<TaskQueueService>();
|
services.AddHostedService<TaskQueueService>();
|
||||||
services.AddSingleton<IBackgroundTaskQueue, BackgroundTaskQueue>();
|
services.AddSingleton<IBackgroundTaskQueue, BackgroundTaskQueue>();
|
||||||
|
services.AddSingleton<ObjectCache, ObjectCache>(c => new ObjectCache(300));
|
||||||
|
services.AddHostedService<CacheCleanerService>();
|
||||||
services.AddScoped<IErrorController, ErrorController>();
|
services.AddScoped<IErrorController, ErrorController>();
|
||||||
|
|
||||||
// Add Tracking Filter scopes
|
// Add Tracking Filter scopes
|
||||||
|
24
Utilities/CacheEntry.cs
Normal file
24
Utilities/CacheEntry.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Teknik.Utilities
|
||||||
|
{
|
||||||
|
public class CacheEntry
|
||||||
|
{
|
||||||
|
public bool RollingExpiration { get; set; }
|
||||||
|
public TimeSpan CacheTime { get; set; }
|
||||||
|
public DateTime LastUpdate { get; set; }
|
||||||
|
public object Data { get; set; }
|
||||||
|
|
||||||
|
public CacheEntry(bool rollingExpiration, TimeSpan cacheTime, DateTime lastUpdate, object data)
|
||||||
|
{
|
||||||
|
RollingExpiration = rollingExpiration;
|
||||||
|
CacheTime = cacheTime;
|
||||||
|
LastUpdate = lastUpdate;
|
||||||
|
Data = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,9 +15,9 @@ namespace Teknik.Utilities.Cryptography
|
|||||||
public AesCounterMode(PooledArray initialCounter)
|
public AesCounterMode(PooledArray initialCounter)
|
||||||
{
|
{
|
||||||
if (initialCounter == null) throw new ArgumentNullException("counter");
|
if (initialCounter == null) throw new ArgumentNullException("counter");
|
||||||
if (initialCounter.Array.Length != _BlockSize)
|
if (initialCounter.Length != _BlockSize)
|
||||||
throw new ArgumentException(String.Format("Counter size must be same as block size (actual: {0}, expected: {1})",
|
throw new ArgumentException(String.Format("Counter size must be same as block size (actual: {0}, expected: {1})",
|
||||||
initialCounter.Array.Length, _BlockSize));
|
initialCounter.Length, _BlockSize));
|
||||||
|
|
||||||
// Generate a new instance of the Aes Algorithm in ECB mode with no padding
|
// Generate a new instance of the Aes Algorithm in ECB mode with no padding
|
||||||
_Algo = Aes.Create();
|
_Algo = Aes.Create();
|
||||||
@ -82,7 +82,7 @@ namespace Teknik.Utilities.Cryptography
|
|||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value >= 0 && value < _EncryptedCounter.Array.Length)
|
if (value >= 0 && value < _EncryptedCounter.Length)
|
||||||
{
|
{
|
||||||
_CounterPosition = value;
|
_CounterPosition = value;
|
||||||
}
|
}
|
||||||
@ -97,14 +97,14 @@ namespace Teknik.Utilities.Cryptography
|
|||||||
if (initialCounter == null) throw new ArgumentNullException("counter");
|
if (initialCounter == null) throw new ArgumentNullException("counter");
|
||||||
|
|
||||||
// Check lengths
|
// Check lengths
|
||||||
if (initialCounter.Array.Length != symmetricAlgorithm.BlockSize / 8)
|
if (initialCounter.Length != symmetricAlgorithm.BlockSize / 8)
|
||||||
throw new ArgumentException(String.Format("Counter size must be same as block size (actual: {0}, expected: {1})",
|
throw new ArgumentException(String.Format("Counter size must be same as block size (actual: {0}, expected: {1})",
|
||||||
initialCounter.Array.Length, symmetricAlgorithm.BlockSize / 8));
|
initialCounter.Length, symmetricAlgorithm.BlockSize / 8));
|
||||||
|
|
||||||
_BlockSize = symmetricAlgorithm.BlockSize;
|
_BlockSize = symmetricAlgorithm.BlockSize;
|
||||||
|
|
||||||
// Initialize Counter
|
// Initialize Counter
|
||||||
_Counter = new PooledArray(initialCounter.Array.Length);
|
_Counter = new PooledArray(initialCounter.Length);
|
||||||
initialCounter.Array.CopyTo(_Counter.Array, 0);
|
initialCounter.Array.CopyTo(_Counter.Array, 0);
|
||||||
|
|
||||||
// Initialize the encrypted counter
|
// Initialize the encrypted counter
|
||||||
@ -144,7 +144,7 @@ namespace Teknik.Utilities.Cryptography
|
|||||||
for (var i = 0; i < inputCount; i++)
|
for (var i = 0; i < inputCount; i++)
|
||||||
{
|
{
|
||||||
// Encrypt the counter if we have reached the end, or
|
// Encrypt the counter if we have reached the end, or
|
||||||
if (_CounterPosition >= _EncryptedCounter.Array.Length)
|
if (_CounterPosition >= _EncryptedCounter.Length)
|
||||||
{
|
{
|
||||||
//Reset current counter position
|
//Reset current counter position
|
||||||
_CounterPosition = 0;
|
_CounterPosition = 0;
|
||||||
@ -178,7 +178,7 @@ namespace Teknik.Utilities.Cryptography
|
|||||||
for (var i = 0; i < inputCount; i++)
|
for (var i = 0; i < inputCount; i++)
|
||||||
{
|
{
|
||||||
// Encrypt the counter if we have reached the end, or
|
// Encrypt the counter if we have reached the end, or
|
||||||
if (_CounterPosition >= _EncryptedCounter.Array.Length)
|
if (_CounterPosition >= _EncryptedCounter.Length)
|
||||||
{
|
{
|
||||||
//Reset current counter position
|
//Reset current counter position
|
||||||
_CounterPosition = 0;
|
_CounterPosition = 0;
|
||||||
@ -203,7 +203,7 @@ namespace Teknik.Utilities.Cryptography
|
|||||||
public void EncryptCounter()
|
public void EncryptCounter()
|
||||||
{
|
{
|
||||||
// Encrypt the current counter to the encrypted counter
|
// Encrypt the current counter to the encrypted counter
|
||||||
_CounterEncryptor.TransformBlock(_Counter.Array, 0, _Counter.Array.Length, _EncryptedCounter.Array, 0);
|
_CounterEncryptor.TransformBlock(_Counter.Array, 0, _Counter.Length, _EncryptedCounter.Array, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetCounter()
|
public void ResetCounter()
|
||||||
@ -214,7 +214,7 @@ namespace Teknik.Utilities.Cryptography
|
|||||||
|
|
||||||
public void IncrementCounter()
|
public void IncrementCounter()
|
||||||
{
|
{
|
||||||
int j = _Counter.Array.Length;
|
int j = _Counter.Length;
|
||||||
while (--j >= 0 && ++_Counter.Array[j] == 0)
|
while (--j >= 0 && ++_Counter.Array[j] == 0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -3,61 +3,112 @@ using System.Collections.Concurrent;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Teknik.Utilities
|
namespace Teknik.Utilities
|
||||||
{
|
{
|
||||||
public class ObjectCache
|
public class ObjectCache
|
||||||
{
|
{
|
||||||
private readonly static ConcurrentDictionary<string, Tuple<DateTime, object>> objectCache = new ConcurrentDictionary<string, Tuple<DateTime, object>>();
|
private readonly int _defaultCacheTime;
|
||||||
private readonly int _cacheSeconds;
|
private static object _cacheLock = new object();
|
||||||
|
|
||||||
public ObjectCache(int cacheSeconds)
|
private readonly static ConcurrentDictionary<string, CacheEntry> _objectCache = new ConcurrentDictionary<string, CacheEntry>();
|
||||||
|
|
||||||
|
public ObjectCache(int defaultCacheTime)
|
||||||
{
|
{
|
||||||
_cacheSeconds = cacheSeconds;
|
_defaultCacheTime = defaultCacheTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public T GetObject<T>(string key, Func<string, T> getObjectFunc)
|
public T AddOrGetObject<T>(string key, Func<string, T> getObjectFunc)
|
||||||
|
{
|
||||||
|
return AddOrGetObject(key, new TimeSpan(0, 0, _defaultCacheTime), getObjectFunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public T AddOrGetObject<T>(string key, TimeSpan cacheTime, Func<string, T> getObjectFunc)
|
||||||
{
|
{
|
||||||
T foundObject;
|
T foundObject;
|
||||||
var cacheDate = DateTime.UtcNow;
|
lock (_cacheLock)
|
||||||
if (objectCache.TryGetValue(key, out var result) &&
|
|
||||||
result.Item1 > cacheDate.Subtract(new TimeSpan(0, 0, _cacheSeconds)))
|
|
||||||
{
|
{
|
||||||
return (T)result.Item2;
|
if (_objectCache.TryGetValue(GenerateKey<T>(key), out var result) &&
|
||||||
|
CacheValid(result))
|
||||||
|
{
|
||||||
|
if (result.RollingExpiration)
|
||||||
|
{
|
||||||
|
result.LastUpdate = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
return (T)result.Data;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
foundObject = getObjectFunc(key);
|
foundObject = getObjectFunc(key);
|
||||||
// Update the cache for this key
|
// Update the cache for this key
|
||||||
if (foundObject != null)
|
if (foundObject != null)
|
||||||
UpdateObject(key, foundObject, cacheDate);
|
UpdateObject(key, foundObject, cacheTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return foundObject;
|
return foundObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateObject<T>(string key, T update)
|
public void UpdateObject<T>(string key, T data)
|
||||||
{
|
{
|
||||||
UpdateObject(key, update, DateTime.UtcNow);
|
UpdateObject(key, data, null, DateTime.UtcNow);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateObject<T>(string key, T update, DateTime cacheTime)
|
public void UpdateObject<T>(string key, T data, TimeSpan cacheTime)
|
||||||
{
|
{
|
||||||
objectCache[key] = new Tuple<DateTime, object>(cacheTime, update);
|
UpdateObject(key, data, cacheTime, DateTime.UtcNow);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeleteObject(string key)
|
public void UpdateObject<T>(string key, T data, TimeSpan? cacheTime, DateTime lastUpdate)
|
||||||
{
|
{
|
||||||
objectCache.TryRemove(key, out _);
|
if (_objectCache.TryGetValue(GenerateKey<T>(key), out var result))
|
||||||
|
{
|
||||||
|
result.Data = data;
|
||||||
|
if (cacheTime.HasValue)
|
||||||
|
result.CacheTime = cacheTime.Value;
|
||||||
|
result.LastUpdate = lastUpdate;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!cacheTime.HasValue)
|
||||||
|
cacheTime = new TimeSpan(0, 0, _defaultCacheTime);
|
||||||
|
_objectCache[GenerateKey<T>(key)] = CreateCacheEntry(data, cacheTime.Value, lastUpdate);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CacheValid(string key)
|
public void DeleteObject<T>(string key)
|
||||||
{
|
{
|
||||||
if (objectCache.TryGetValue(key, out var result) &&
|
_objectCache.TryRemove(GenerateKey<T>(key), out _);
|
||||||
result.Item1 > DateTime.UtcNow.Subtract(new TimeSpan(0, 0, _cacheSeconds)))
|
}
|
||||||
return true;
|
|
||||||
return false;
|
public bool CacheValid(CacheEntry entry)
|
||||||
|
{
|
||||||
|
return entry.LastUpdate > DateTime.UtcNow.Subtract(entry.CacheTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CleanCache()
|
||||||
|
{
|
||||||
|
lock (_cacheLock)
|
||||||
|
{
|
||||||
|
foreach (var obj in _objectCache)
|
||||||
|
{
|
||||||
|
if (!CacheValid(obj.Value))
|
||||||
|
_objectCache.TryRemove(obj.Key, out _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GenerateKey<T>(string key)
|
||||||
|
{
|
||||||
|
var typeKey = typeof(T).ToString();
|
||||||
|
return $"{typeKey}.{key}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public CacheEntry CreateCacheEntry<T>(T data, TimeSpan cacheTime, DateTime lastUpdate)
|
||||||
|
{
|
||||||
|
return new CacheEntry(false, cacheTime, lastUpdate, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,15 +13,19 @@ namespace Teknik.Utilities
|
|||||||
|
|
||||||
public byte[] Array { get; private set; }
|
public byte[] Array { get; private set; }
|
||||||
|
|
||||||
|
public readonly int Length;
|
||||||
|
|
||||||
public PooledArray(int size)
|
public PooledArray(int size)
|
||||||
{
|
{
|
||||||
Array = _arrayPool.Rent(size);
|
Array = _arrayPool.Rent(size);
|
||||||
|
Length = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PooledArray(byte[] array)
|
public PooledArray(byte[] array)
|
||||||
{
|
{
|
||||||
Array = _arrayPool.Rent(array.Length);
|
Array = _arrayPool.Rent(array.Length);
|
||||||
array.CopyTo(Array, 0);
|
array.CopyTo(Array, 0);
|
||||||
|
Length = array.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
Loading…
Reference in New Issue
Block a user