diff --git a/server/AyaNova/Controllers/GlobalOpsSettingsController.cs b/server/AyaNova/Controllers/GlobalOpsSettingsController.cs
index 144a1ab0..7d409bad 100644
--- a/server/AyaNova/Controllers/GlobalOpsSettingsController.cs
+++ b/server/AyaNova/Controllers/GlobalOpsSettingsController.cs
@@ -13,7 +13,7 @@ namespace AyaNova.Api.Controllers
[ApiController]
[ApiVersion("8.0")]
- [Route("api/v{version:apiVersion}/global-biz-setting")]
+ [Route("api/v{version:apiVersion}/global-ops-setting")]
[Produces("application/json")]
[Authorize]
public class GlobalOpsSettingsController : ControllerBase
@@ -40,7 +40,7 @@ namespace AyaNova.Api.Controllers
///
/// Get GlobalOpsSettings
///
- /// Global settings object
+ /// Global ops settings object
[HttpGet]
public async Task GetGlobalOpsSettings()
{
@@ -74,16 +74,11 @@ namespace AyaNova.Api.Controllers
{
if (serverState.IsClosed)
return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
-
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
-
- //Instantiate the business object handler
GlobalOpsSettingsBiz biz = GlobalOpsSettingsBiz.GetBiz(ct, HttpContext);
-
- if (!Authorized.HasModifyRole(HttpContext.Items, biz.BizType))
+ if (!Authorized.HasModifyRole(HttpContext.Items, biz.BizType))
return StatusCode(403, new ApiNotAuthorizedResponse());
-
try
{
if (!await biz.ReplaceAsync(global))
@@ -101,32 +96,24 @@ namespace AyaNova.Api.Controllers
///
/// Global settings object
[HttpGet("client")]
- public ActionResult GetClientGlobalOpsSettings()
+ public async Task GetClientGlobalOpsSettings()
{
+ //NOTE: currently this looks like a dupe of get above and it is
+ //but it's kept here in case want to return a subset of the settings only to client users
+ //where some internal server only stuff might not be desired to send to user
+
if (serverState.IsClosed)
- return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
-
- //Instantiate the business object handler
- // GlobalOpsSettingsBiz biz = GlobalOpsSettingsBiz.GetBiz(ct, HttpContext);
-
- //this route is available to any logged in user as it contains a subset of limited options relevant to any logged in user
- // if (!Authorized.HasReadFullRole(HttpContext.Items, biz.BizType))
- // return StatusCode(403, new ApiNotAuthorizedResponse());
-
+ return StatusCode(503, new ApiErrorResponse(serverState.ApiErrorCode, null, serverState.Reason));
+ GlobalOpsSettingsBiz biz = GlobalOpsSettingsBiz.GetBiz(ct, HttpContext);
+ //this route is available to Ops role user only
+ if (!Authorized.HasReadFullRole(HttpContext.Items, biz.BizType))
+ return StatusCode(403, new ApiNotAuthorizedResponse());
if (!ModelState.IsValid)
return BadRequest(new ApiErrorResponse(ModelState));
-
- // var o = await biz.GetAsync();
- // if (o == null)
- // return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
-
- //new object with only relevant items in it
- var ret = new
- {
- SearchCaseSensitiveOnly = AyaNova.Util.ServerGlobalOpsSettings.SearchCaseSensitiveOnly
- };
-
- return Ok(ApiOkResponse.Response(ret));
+ var o = await biz.GetAsync();
+ if (o == null)
+ return NotFound(new ApiErrorResponse(ApiErrorCode.NOT_FOUND));
+ return Ok(ApiOkResponse.Response(o));
}
}//eoc
diff --git a/server/AyaNova/biz/AyaType.cs b/server/AyaNova/biz/AyaType.cs
index 1b41148f..16a7aaa6 100644
--- a/server/AyaNova/biz/AyaType.cs
+++ b/server/AyaNova/biz/AyaType.cs
@@ -102,7 +102,8 @@ namespace AyaNova.Biz
[CoreBizObject]
WorkOrderTemplate = 45,
[CoreBizObject]
- WorkOrderTemplateItem = 46
+ WorkOrderTemplateItem = 46,
+ GlobalOps=47
//NOTE: New objects added here need to also be added to the following classes:
diff --git a/server/AyaNova/biz/BizRoles.cs b/server/AyaNova/biz/BizRoles.cs
index 53475512..322d55f2 100644
--- a/server/AyaNova/biz/BizRoles.cs
+++ b/server/AyaNova/biz/BizRoles.cs
@@ -339,6 +339,15 @@ namespace AyaNova.Biz
});
+ ////////////////////////////////////////////////////////////
+ //GLOBAL OPS SETTINGS
+ //
+ roles.Add(AyaType.GlobalOps, new BizRoleSet()
+ {
+ Change = AuthorizationRoles.OpsAdminFull,
+ ReadFullRecord = AuthorizationRoles.OpsAdminLimited
+ });
+
////////////////////////////////////////////////////////////
//USER
diff --git a/server/AyaNova/biz/GlobalOpsSettingsBiz.cs b/server/AyaNova/biz/GlobalOpsSettingsBiz.cs
index 3e696cfa..a5515b83 100644
--- a/server/AyaNova/biz/GlobalOpsSettingsBiz.cs
+++ b/server/AyaNova/biz/GlobalOpsSettingsBiz.cs
@@ -1,3 +1,4 @@
+using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using AyaNova.Util;
@@ -16,7 +17,7 @@ namespace AyaNova.Biz
UserId = currentUserId;
UserTranslationId = userTranslationId;
CurrentUserRoles = UserRoles;
- BizType = AyaType.Global;
+ BizType = AyaType.GlobalOps;
}
internal static GlobalOpsSettingsBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null)
@@ -39,16 +40,14 @@ namespace AyaNova.Biz
//first try to fetch from db
var ret = await ct.GlobalOpsSettings.SingleOrDefaultAsync(m => m.Id == 1);
if (logTheGetEvent && ret != null)
- {
- //Log
+
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, 1, BizType, AyaEvent.Retrieved), ct);
- }
+
//not in db then get the default
if (ret == null)
- {
throw new System.Exception("GlobalOpsSettingsBiz::GetAsync -> Global OPS settings object not found in database!!");
- }
+
return ret;
}
@@ -59,23 +58,26 @@ namespace AyaNova.Biz
//
//put
- internal async Task ReplaceAsync(GlobalOpsSettings inObj)
+ internal async Task ReplaceAsync(GlobalOpsSettings putObject)
{
- var dbObj = await ct.GlobalOpsSettings.FirstOrDefaultAsync(m => m.Id == 1);
- if (dbObj == null)
+ var dbObject = await ct.GlobalOpsSettings.FirstOrDefaultAsync(m => m.Id == 1);
+ if (dbObject == null)
throw new System.Exception("GlobalOpsSettingsBiz::ReplaceAsync -> Global settings object not found in database!!");
- CopyObject.Copy(inObj, dbObj, "Id");
- ct.Entry(dbObj).OriginalValues["Concurrency"] = inObj.Concurrency;
-
- Validate(dbObj);
+ //If backup time has changed then reset last backup as well as it might block from taking effect
+ if (putObject.BackupTime.Hour != dbObject.BackupTime.Hour && putObject.BackupTime.Minute != dbObject.BackupTime.Minute)
+ {
+ putObject.LastBackup = DateTime.MinValue;
+ }
+ CopyObject.Copy(putObject, dbObject, "Id");
+ ct.Entry(dbObject).OriginalValues["Concurrency"] = putObject.Concurrency;
+ Validate(dbObject);
if (HasErrors)
return false;
await ct.SaveChangesAsync();
- //Log modification and save context
await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, 1, BizType, AyaEvent.Modified), ct);
//Update the static copy for the server
- ServerGlobalOpsSettings.Initialize(dbObj);
+ ServerGlobalOpsSettings.Initialize(dbObject);
return true;
}
diff --git a/server/AyaNova/generator/CoreJobBackup.cs b/server/AyaNova/generator/CoreJobBackup.cs
index 9c9e1cf6..786fb3fa 100644
--- a/server/AyaNova/generator/CoreJobBackup.cs
+++ b/server/AyaNova/generator/CoreJobBackup.cs
@@ -4,6 +4,7 @@ using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using AyaNova.Models;
+using AyaNova.Util;
namespace AyaNova.Biz
@@ -17,44 +18,43 @@ namespace AyaNova.Biz
internal static class CoreJobBackup
{
private static ILogger log = AyaNova.Util.ApplicationLogging.CreateLogger("CoreJobBackup");
- private static DateTime lastSweep = DateTime.MinValue;
- private static TimeSpan SWEEP_EVERY_INTERVAL = new TimeSpan(0, 30, 0);
- private static TimeSpan SUCCEEDED_JOBS_DELETE_AFTER_THIS_TIMESPAN = new TimeSpan(24, 0, 0);//24 hours
- private static TimeSpan FAILED_JOBS_DELETE_AFTER_THIS_TIMESPAN = new TimeSpan(14, 0, 0, 0);//14 days (gives people time to notice and look into it)
- private static TimeSpan RUNNING_JOBS_BECOME_FAILED_AFTER_THIS_TIMESPAN = new TimeSpan(24, 0, 0);//24 hours (time running jobs are allowed to sit in "running" state before considered failed)
-
+ private static bool BackupIsRunning = false;
////////////////////////////////////////////////////////////////////////////////////////////////
//
//
public static async Task DoWorkAsync(AyContext ct)
{
+ if (BackupIsRunning) return;
- //This will get triggered roughly every minute, but we don't want to sweep that frequently
- if (DateTime.UtcNow - lastSweep < SWEEP_EVERY_INTERVAL)
- return;
+ //get NOW in utc
+ DateTime utcNow = DateTime.UtcNow;
+ //what time should we backup today?
+ DateTime todayBackupTime = new DateTime(utcNow.Year, utcNow.Month, utcNow.Day, ServerGlobalOpsSettings.BackupTime.Hour, ServerGlobalOpsSettings.BackupTime.Minute, 0, DateTimeKind.Utc);//first start with NOW
+
+ //Are we there yet?
+ if (utcNow < todayBackupTime) return;//nope
+
+ //Yes, we've passed into the backup window time, but that's also true if we just ran the backup as well so
+ //need to check for that as well...
+
+ //Has last backup run more than 24 hours ago?
+ if (ServerGlobalOpsSettings.LastBackup > utcNow.AddHours(-24))
+ return;//nope, so we have already run today's backup
+
+ //Ok, we're into backup time and it's been more than 24 hours since it last ran so let's do this...
+ BackupIsRunning = true;
log.LogTrace("Backup starting");
-
- //lock the server
- //serverState.SetSystemLock(msg);
- //SWEEP SUCCESSFUL JOBS
- //calculate cutoff to delete
- DateTime dtDeleteCutoff = DateTime.UtcNow - SUCCEEDED_JOBS_DELETE_AFTER_THIS_TIMESPAN;
- await sweepAsync(ct, dtDeleteCutoff, JobStatus.Completed);
- //SWEEP FAILED JOBS
- //calculate cutoff to delete
- dtDeleteCutoff = DateTime.UtcNow - FAILED_JOBS_DELETE_AFTER_THIS_TIMESPAN;
- await sweepAsync(ct, dtDeleteCutoff, JobStatus.Failed);
+ //*************
+ //BACKUP TORA TORA TORA!
+ //***************
- //KILL STUCK JOBS
- //calculate cutoff to delete
- DateTime dtRunningDeadline = DateTime.UtcNow - RUNNING_JOBS_BECOME_FAILED_AFTER_THIS_TIMESPAN;
- await killStuckJobsAsync(ct, dtRunningDeadline);
-
- lastSweep = DateTime.UtcNow;
+ //Update last backup
+ BackupIsRunning = false;
+ log.LogTrace("Backup completed");
}
diff --git a/server/AyaNova/resource/de.json b/server/AyaNova/resource/de.json
index 03a52454..091b57c7 100644
--- a/server/AyaNova/resource/de.json
+++ b/server/AyaNova/resource/de.json
@@ -445,6 +445,7 @@
"ContractRate": "Vertragssatz",
"DispatchZone": "Zuweisungszone",
"Global": "Global",
+ "GlobalOps": "Global ops",
"GlobalWikiPage": "Global Wiki page",
"GridFilter": "GridFilter",
"HeadOffice": "Hauptsitz",
diff --git a/server/AyaNova/resource/en.json b/server/AyaNova/resource/en.json
index c5b73b1c..e5d99aa1 100644
--- a/server/AyaNova/resource/en.json
+++ b/server/AyaNova/resource/en.json
@@ -445,6 +445,7 @@
"ContractRate": "Contract rate",
"DispatchZone": "Dispatch Zone",
"Global": "Global",
+ "GlobalOps": "Global ops",
"GlobalWikiPage": "Global Wiki page",
"GridFilter": "GridFilter",
"HeadOffice": "Head Office",
diff --git a/server/AyaNova/resource/es.json b/server/AyaNova/resource/es.json
index 06327cb5..eca7076c 100644
--- a/server/AyaNova/resource/es.json
+++ b/server/AyaNova/resource/es.json
@@ -445,6 +445,7 @@
"ContractRate": "Tarifa de contrato",
"DispatchZone": "Zona de reparto",
"Global": "Global",
+ "GlobalOps": "Global ops",
"GlobalWikiPage": "Global Wiki page",
"GridFilter": "GridFilter",
"HeadOffice": "Sede",
diff --git a/server/AyaNova/resource/fr.json b/server/AyaNova/resource/fr.json
index 5a99f0c1..26604956 100644
--- a/server/AyaNova/resource/fr.json
+++ b/server/AyaNova/resource/fr.json
@@ -445,6 +445,7 @@
"ContractRate": "Tarif de contrat",
"DispatchZone": "Zone d'expédition",
"Global": "Général",
+ "GlobalOps": "Global ops",
"GlobalWikiPage": "Global Wiki page",
"GridFilter": "GridFilter",
"HeadOffice": "Siège social",