completed background job scheduler

This commit is contained in:
Shaun Walker
2019-11-15 08:42:31 -05:00
parent b4cd038e17
commit 25d2c6596d
41 changed files with 1248 additions and 554 deletions

View File

@ -0,0 +1,105 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Oqtane.Repository;
using Oqtane.Models;
using Oqtane.Shared;
using Oqtane.Infrastructure;
using System;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
namespace Oqtane.Controllers
{
[Route("{site}/api/[controller]")]
public class JobController : Controller
{
private readonly IJobRepository Jobs;
private readonly ILogManager logger;
private readonly IServiceProvider ServiceProvider;
public JobController(IJobRepository Jobs, ILogManager logger, IServiceProvider ServiceProvider)
{
this.Jobs = Jobs;
this.logger = logger;
this.ServiceProvider = ServiceProvider;
}
// GET: api/<controller>
[HttpGet]
public IEnumerable<Job> Get()
{
return Jobs.GetJobs();
}
// GET api/<controller>/5
[HttpGet("{id}")]
public Job Get(int id)
{
return Jobs.GetJob(id);
}
// POST api/<controller>
[HttpPost]
[Authorize(Roles = Constants.HostRole)]
public Job Post([FromBody] Job Job)
{
if (ModelState.IsValid)
{
Job = Jobs.AddJob(Job);
logger.Log(LogLevel.Information, this, LogFunction.Create, "Job Added {Job}", Job);
}
return Job;
}
// PUT api/<controller>/5
[HttpPut("{id}")]
[Authorize(Roles = Constants.HostRole)]
public Job Put(int id, [FromBody] Job Job)
{
if (ModelState.IsValid)
{
Job = Jobs.UpdateJob(Job);
logger.Log(LogLevel.Information, this, LogFunction.Update, "Job Updated {Job}", Job);
}
return Job;
}
// DELETE api/<controller>/5
[HttpDelete("{id}")]
[Authorize(Roles = Constants.HostRole)]
public void Delete(int id)
{
Jobs.DeleteJob(id);
logger.Log(LogLevel.Information, this, LogFunction.Delete, "Job Deleted {JobId}", id);
}
// GET api/<controller>/start
[HttpGet("start/{id}")]
[Authorize(Roles = Constants.HostRole)]
public void Start(int id)
{
Job job = Jobs.GetJob(id);
Type jobtype = Type.GetType(job.JobType);
if (jobtype != null)
{
var jobobject = ActivatorUtilities.CreateInstance(ServiceProvider, jobtype);
((IHostedService)jobobject).StartAsync(new System.Threading.CancellationToken());
}
}
// GET api/<controller>/stop
[HttpGet("stop/{id}")]
[Authorize(Roles = Constants.HostRole)]
public void Stop(int id)
{
Job job = Jobs.GetJob(id);
Type jobtype = Type.GetType(job.JobType);
if (jobtype != null)
{
var jobobject = ActivatorUtilities.CreateInstance(ServiceProvider, jobtype);
((IHostedService)jobobject).StopAsync(new System.Threading.CancellationToken());
}
}
}
}

View File

@ -9,55 +9,57 @@ using Oqtane.Infrastructure;
namespace Oqtane.Controllers
{
[Route("{site}/api/[controller]")]
public class ScheduleController : Controller
public class JobLogController : Controller
{
private readonly IScheduleRepository Schedules;
private readonly IJobLogRepository JobLogs;
private readonly ILogManager logger;
public ScheduleController(IScheduleRepository Schedules, ILogManager logger)
public JobLogController(IJobLogRepository JobLogs, ILogManager logger)
{
this.Schedules = Schedules;
this.JobLogs = JobLogs;
this.logger = logger;
}
// GET: api/<controller>
[HttpGet]
public IEnumerable<Schedule> Get()
[Authorize(Roles = Constants.HostRole)]
public IEnumerable<JobLog> Get()
{
return Schedules.GetSchedules();
return JobLogs.GetJobLogs();
}
// GET api/<controller>/5
[HttpGet("{id}")]
public Schedule Get(int id)
[Authorize(Roles = Constants.HostRole)]
public JobLog Get(int id)
{
return Schedules.GetSchedule(id);
return JobLogs.GetJobLog(id);
}
// POST api/<controller>
[HttpPost]
[Authorize(Roles = Constants.HostRole)]
public Schedule Post([FromBody] Schedule Schedule)
public JobLog Post([FromBody] JobLog JobLog)
{
if (ModelState.IsValid)
{
Schedule = Schedules.AddSchedule(Schedule);
logger.Log(LogLevel.Information, this, LogFunction.Create, "Schedule Added {Schedule}", Schedule);
JobLog = JobLogs.AddJobLog(JobLog);
logger.Log(LogLevel.Information, this, LogFunction.Create, "Job Log Added {JobLog}", JobLog);
}
return Schedule;
return JobLog;
}
// PUT api/<controller>/5
[HttpPut("{id}")]
[Authorize(Roles = Constants.HostRole)]
public Schedule Put(int id, [FromBody] Schedule Schedule)
public JobLog Put(int id, [FromBody] JobLog JobLog)
{
if (ModelState.IsValid)
{
Schedule = Schedules.UpdateSchedule(Schedule);
logger.Log(LogLevel.Information, this, LogFunction.Update, "Schedule Updated {Schedule}", Schedule);
JobLog = JobLogs.UpdateJobLog(JobLog);
logger.Log(LogLevel.Information, this, LogFunction.Update, "Job Log Updated {JobLog}", JobLog);
}
return Schedule;
return JobLog;
}
// DELETE api/<controller>/5
@ -65,8 +67,8 @@ namespace Oqtane.Controllers
[Authorize(Roles = Constants.HostRole)]
public void Delete(int id)
{
Schedules.DeleteSchedule(id);
logger.Log(LogLevel.Information, this, LogFunction.Delete, "Schedule Deleted {ScheduleId}", id);
JobLogs.DeleteJobLog(id);
logger.Log(LogLevel.Information, this, LogFunction.Delete, "Job Log Deleted {JobLogId}", id);
}
}
}

View File

@ -1,74 +0,0 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Oqtane.Repository;
using Oqtane.Models;
using Oqtane.Shared;
using Oqtane.Infrastructure;
namespace Oqtane.Controllers
{
[Route("{site}/api/[controller]")]
public class ScheduleLogController : Controller
{
private readonly IScheduleLogRepository ScheduleLogs;
private readonly ILogManager logger;
public ScheduleLogController(IScheduleLogRepository ScheduleLogs, ILogManager logger)
{
this.ScheduleLogs = ScheduleLogs;
this.logger = logger;
}
// GET: api/<controller>
[HttpGet]
[Authorize(Roles = Constants.HostRole)]
public IEnumerable<ScheduleLog> Get()
{
return ScheduleLogs.GetScheduleLogs();
}
// GET api/<controller>/5
[HttpGet("{id}")]
[Authorize(Roles = Constants.HostRole)]
public ScheduleLog Get(int id)
{
return ScheduleLogs.GetScheduleLog(id);
}
// POST api/<controller>
[HttpPost]
[Authorize(Roles = Constants.HostRole)]
public ScheduleLog Post([FromBody] ScheduleLog ScheduleLog)
{
if (ModelState.IsValid)
{
ScheduleLog = ScheduleLogs.AddScheduleLog(ScheduleLog);
logger.Log(LogLevel.Information, this, LogFunction.Create, "Schedule Log Added {ScheduleLog}", ScheduleLog);
}
return ScheduleLog;
}
// PUT api/<controller>/5
[HttpPut("{id}")]
[Authorize(Roles = Constants.HostRole)]
public ScheduleLog Put(int id, [FromBody] ScheduleLog ScheduleLog)
{
if (ModelState.IsValid)
{
ScheduleLog = ScheduleLogs.UpdateScheduleLog(ScheduleLog);
logger.Log(LogLevel.Information, this, LogFunction.Update, "Schedule Log Updated {ScheduleLog}", ScheduleLog);
}
return ScheduleLog;
}
// DELETE api/<controller>/5
[HttpDelete("{id}")]
[Authorize(Roles = Constants.HostRole)]
public void Delete(int id)
{
ScheduleLogs.DeleteScheduleLog(id);
logger.Log(LogLevel.Information, this, LogFunction.Delete, "Schedule Log Deleted {ScheduleLogId}", id);
}
}
}

View File

@ -1,92 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Oqtane.Models;
using Oqtane.Repository;
using Oqtane.Shared;
namespace Oqtane.Infrastructure
{
public abstract class HostedServiceBase : IHostedService, IDisposable
{
private Task ExecutingTask;
private readonly CancellationTokenSource CancellationTokenSource = new CancellationTokenSource();
private readonly IServiceScopeFactory ServiceScopeFactory;
public HostedServiceBase(IServiceScopeFactory ServiceScopeFactory)
{
this.ServiceScopeFactory = ServiceScopeFactory;
}
// abstract method must be overridden by job
public abstract void ExecuteJob(IServiceProvider provider);
protected async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
while (!stoppingToken.IsCancellationRequested)
{
// allows consumption of scoped services
using (var scope = ServiceScopeFactory.CreateScope())
{
string JobType = Utilities.GetFullTypeName(this.GetType().AssemblyQualifiedName);
IScheduleRepository ScheduleRepository = scope.ServiceProvider.GetRequiredService<IScheduleRepository>();
List<Schedule> schedules = ScheduleRepository.GetSchedules().ToList();
Schedule schedule = schedules.Where(item => item.JobType == JobType).FirstOrDefault();
if (schedule != null && schedule.IsActive)
{
ExecuteJob(scope.ServiceProvider);
}
}
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
}
catch
{
// can occur during the initial installation as there is no DBContext
}
}
public Task StartAsync(CancellationToken cancellationToken)
{
ExecutingTask = ExecuteAsync(CancellationTokenSource.Token);
if (ExecutingTask.IsCompleted)
{
return ExecutingTask;
}
return Task.CompletedTask;
}
public async Task StopAsync(CancellationToken CancellationToken)
{
if (ExecutingTask == null)
{
return;
}
try
{
CancellationTokenSource.Cancel();
}
finally
{
await Task.WhenAny(ExecutingTask, Task.Delay(Timeout.Infinite, CancellationToken));
}
}
public void Dispose()
{
CancellationTokenSource.Cancel();
}
}
}

View File

@ -0,0 +1,197 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Oqtane.Models;
using Oqtane.Repository;
using Oqtane.Shared;
namespace Oqtane.Infrastructure
{
public abstract class HostedServiceBase : IHostedService, IDisposable
{
private Task ExecutingTask;
private readonly CancellationTokenSource CancellationTokenSource = new CancellationTokenSource();
private readonly IServiceScopeFactory ServiceScopeFactory;
public HostedServiceBase(IServiceScopeFactory ServiceScopeFactory)
{
this.ServiceScopeFactory = ServiceScopeFactory;
}
// abstract method must be overridden
public abstract string ExecuteJob(IServiceProvider provider);
protected async Task ExecuteAsync(CancellationToken stoppingToken)
{
try
{
while (!stoppingToken.IsCancellationRequested)
{
using (var scope = ServiceScopeFactory.CreateScope())
{
// get name of job
string JobType = Utilities.GetFullTypeName(this.GetType().AssemblyQualifiedName);
// load jobs and find current job
IJobRepository Jobs = scope.ServiceProvider.GetRequiredService<IJobRepository>();
Job Job = Jobs.GetJobs().Where(item => item.JobType == JobType).FirstOrDefault();
if (Job != null && Job.IsEnabled && !Job.IsExecuting)
{
// set next execution date
if (Job.NextExecution == null)
{
if (Job.StartDate != null)
{
Job.NextExecution = Job.StartDate;
}
else
{
Job.NextExecution = DateTime.Now;
}
}
// determine if the job should be run
if (Job.NextExecution <= DateTime.Now && (Job.EndDate == null || Job.EndDate >= DateTime.Now))
{
IJobLogRepository JobLogs = scope.ServiceProvider.GetRequiredService<IJobLogRepository>();
// create a job log entry
JobLog log = new JobLog();
log.JobId = Job.JobId;
log.StartDate = DateTime.Now;
log.FinishDate = null;
log.Succeeded = false;
log.Notes = "";
log = JobLogs.AddJobLog(log);
// update the job to indicate it is running
Job.IsExecuting = true;
Jobs.UpdateJob(Job);
// execute the job
try
{
log.Notes = ExecuteJob(scope.ServiceProvider);
log.Succeeded = true;
}
catch (Exception ex)
{
log.Notes = ex.Message;
log.Succeeded = false;
}
// update the job log
log.FinishDate = DateTime.Now;
JobLogs.UpdateJobLog(log);
// update the job
Job.NextExecution = CalculateNextExecution(Job.NextExecution.Value, Job.Frequency, Job.Interval);
Job.IsExecuting = false;
Jobs.UpdateJob(Job);
// trim the job log
List<JobLog> logs = JobLogs.GetJobLogs().Where(item => item.JobId == Job.JobId)
.OrderByDescending(item => item.JobLogId).ToList();
for (int i = logs.Count; i > Job.RetentionHistory; i--)
{
JobLogs.DeleteJobLog(logs[i - 1].JobLogId);
}
}
}
}
// wait 1 minute
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
}
catch
{
// can occur during the initial installation as there is no DBContext
}
}
private DateTime CalculateNextExecution(DateTime NextExecution, string Frequency, int Interval)
{
switch (Frequency)
{
case "m": // minutes
NextExecution = NextExecution.AddMinutes(Interval);
break;
case "H": // hours
NextExecution = NextExecution.AddHours(Interval);
break;
case "d": // days
NextExecution = NextExecution.AddDays(Interval);
break;
case "M": // months
NextExecution = NextExecution.AddMonths(Interval);
break;
}
if (NextExecution < DateTime.Now)
{
NextExecution = DateTime.Now;
}
return NextExecution;
}
public Task StartAsync(CancellationToken cancellationToken)
{
try
{
// set IsExecuting to false in case this job was forcefully terminated previously
using (var scope = ServiceScopeFactory.CreateScope())
{
string JobType = Utilities.GetFullTypeName(this.GetType().AssemblyQualifiedName);
IJobRepository Jobs = scope.ServiceProvider.GetRequiredService<IJobRepository>();
Job Job = Jobs.GetJobs().Where(item => item.JobType == JobType).FirstOrDefault();
if (Job != null)
{
Job.IsStarted = true;
Job.IsExecuting = false;
Jobs.UpdateJob(Job);
}
}
}
catch
{
// can occur during the initial installation as there is no DBContext
}
ExecutingTask = ExecuteAsync(CancellationTokenSource.Token);
if (ExecutingTask.IsCompleted)
{
return ExecutingTask;
}
return Task.CompletedTask;
}
public async Task StopAsync(CancellationToken CancellationToken)
{
if (ExecutingTask == null)
{
return;
}
try
{
CancellationTokenSource.Cancel();
}
finally
{
await Task.WhenAny(ExecutingTask, Task.Delay(Timeout.Infinite, CancellationToken));
}
}
public void Dispose()
{
CancellationTokenSource.Cancel();
}
}
}

View File

@ -0,0 +1,33 @@
using System;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Oqtane.Models;
using Oqtane.Repository;
using Oqtane.Shared;
namespace Oqtane.Infrastructure
{
public class SampleJob : HostedServiceBase
{
// JobType = "Oqtane.Infrastructure.SampleJob, Oqtane.Server"
public SampleJob(IServiceScopeFactory ServiceScopeFactory) : base(ServiceScopeFactory) {}
public override string ExecuteJob(IServiceProvider provider)
{
// get the first alias for this installation
var Aliases = provider.GetRequiredService<IAliasRepository>();
Alias alias = Aliases.GetAliases().FirstOrDefault();
// use the SiteState to set the Alias explicitly so the tenant can be resolved
var sitestate = provider.GetRequiredService<SiteState>();
sitestate.Alias = alias;
// call a repository service which requires tenant resolution
var Sites = provider.GetRequiredService<ISiteRepository>();
Site site = Sites.GetSites().FirstOrDefault();
return "You Should Include Any Notes Related To The Execution Of The Schedule Job. This Job Simply Reports That The Default Site Is " + site.Name;
}
}
}

View File

@ -1,21 +0,0 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Oqtane.Models;
using Oqtane.Repository;
namespace Oqtane.Infrastructure
{
public class TestJob : HostedServiceBase
{
public TestJob(IServiceScopeFactory ServiceScopeFactory) : base(ServiceScopeFactory) {}
public override void ExecuteJob(IServiceProvider provider)
{
var Tenants = provider.GetRequiredService<ITenantRepository>();
foreach(Tenant tenant in Tenants.GetTenants())
{
// is it possible to set the HttpContext so that DbContextBase will resolve properly for tenants?
}
}
}
}

View File

@ -42,7 +42,7 @@ namespace Oqtane.Repository
ChangeTracker.DetectChanges();
string username = "";
if (accessor.HttpContext.User.Identity.Name != null)
if (accessor.HttpContext != null && accessor.HttpContext.User.Identity.Name != null)
{
username = accessor.HttpContext.User.Identity.Name;
}

View File

@ -18,15 +18,15 @@ namespace Oqtane.Repository
public virtual DbSet<Alias> Alias { get; set; }
public virtual DbSet<Tenant> Tenant { get; set; }
public virtual DbSet<ModuleDefinition> ModuleDefinition { get; set; }
public virtual DbSet<Schedule> Schedule { get; set; }
public virtual DbSet<ScheduleLog> ScheduleLog { get; set; }
public virtual DbSet<Job> Job { get; set; }
public virtual DbSet<JobLog> JobLog { get; set; }
public override int SaveChanges()
{
ChangeTracker.DetectChanges();
string username = "";
if (accessor.HttpContext.User.Identity.Name != null)
if (accessor.HttpContext != null && accessor.HttpContext.User.Identity.Name != null)
{
username = accessor.HttpContext.User.Identity.Name;
}

View File

@ -0,0 +1,14 @@
using System.Collections.Generic;
using Oqtane.Models;
namespace Oqtane.Repository
{
public interface IJobLogRepository
{
IEnumerable<JobLog> GetJobLogs();
JobLog AddJobLog(JobLog JobLog);
JobLog UpdateJobLog(JobLog JobLog);
JobLog GetJobLog(int JobLogId);
void DeleteJobLog(int JobLogId);
}
}

View File

@ -0,0 +1,14 @@
using System.Collections.Generic;
using Oqtane.Models;
namespace Oqtane.Repository
{
public interface IJobRepository
{
IEnumerable<Job> GetJobs();
Job AddJob(Job Job);
Job UpdateJob(Job Job);
Job GetJob(int JobId);
void DeleteJob(int JobId);
}
}

View File

@ -1,14 +0,0 @@
using System.Collections.Generic;
using Oqtane.Models;
namespace Oqtane.Repository
{
public interface IScheduleLogRepository
{
IEnumerable<ScheduleLog> GetScheduleLogs();
ScheduleLog AddScheduleLog(ScheduleLog ScheduleLog);
ScheduleLog UpdateScheduleLog(ScheduleLog ScheduleLog);
ScheduleLog GetScheduleLog(int ScheduleLogId);
void DeleteScheduleLog(int ScheduleLogId);
}
}

View File

@ -1,14 +0,0 @@
using System.Collections.Generic;
using Oqtane.Models;
namespace Oqtane.Repository
{
public interface IScheduleRepository
{
IEnumerable<Schedule> GetSchedules();
Schedule AddSchedule(Schedule Schedule);
Schedule UpdateSchedule(Schedule Schedule);
Schedule GetSchedule(int ScheduleId);
void DeleteSchedule(int ScheduleId);
}
}

View File

@ -0,0 +1,51 @@
using System.Collections.Generic;
using System.Linq;
using Oqtane.Models;
using Microsoft.EntityFrameworkCore;
namespace Oqtane.Repository
{
public class JobLogRepository : IJobLogRepository
{
private MasterDBContext db;
public JobLogRepository(MasterDBContext context)
{
db = context;
}
public IEnumerable<JobLog> GetJobLogs()
{
return db.JobLog
.Include(item => item.Job) // eager load jobs
.ToList();
}
public JobLog AddJobLog(JobLog JobLog)
{
db.JobLog.Add(JobLog);
db.SaveChanges();
return JobLog;
}
public JobLog UpdateJobLog(JobLog JobLog)
{
db.Entry(JobLog).State = EntityState.Modified;
db.SaveChanges();
return JobLog;
}
public JobLog GetJobLog(int JobLogId)
{
return db.JobLog.Include(item => item.Job) // eager load job
.SingleOrDefault(item => item.JobLogId == JobLogId);
}
public void DeleteJobLog(int JobLogId)
{
JobLog Joblog = db.JobLog.Find(JobLogId);
db.JobLog.Remove(Joblog);
db.SaveChanges();
}
}
}

View File

@ -0,0 +1,49 @@
using System.Collections.Generic;
using System.Linq;
using Oqtane.Models;
using Microsoft.EntityFrameworkCore;
using System;
namespace Oqtane.Repository
{
public class JobRepository : IJobRepository
{
private MasterDBContext db;
public JobRepository(MasterDBContext context)
{
db = context;
}
public IEnumerable<Job> GetJobs()
{
return db.Job.ToList();
}
public Job AddJob(Job Job)
{
db.Job.Add(Job);
db.SaveChanges();
return Job;
}
public Job UpdateJob(Job Job)
{
db.Entry(Job).State = EntityState.Modified;
db.SaveChanges();
return Job;
}
public Job GetJob(int JobId)
{
return db.Job.Find(JobId);
}
public void DeleteJob(int JobId)
{
Job Job = db.Job.Find(JobId);
db.Job.Remove(Job);
db.SaveChanges();
}
}
}

View File

@ -1,49 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Oqtane.Models;
using Microsoft.EntityFrameworkCore;
using System;
namespace Oqtane.Repository
{
public class ScheduleLogRepository : IScheduleLogRepository
{
private MasterDBContext db;
public ScheduleLogRepository(MasterDBContext context)
{
db = context;
}
public IEnumerable<ScheduleLog> GetScheduleLogs()
{
return db.ScheduleLog.ToList();
}
public ScheduleLog AddScheduleLog(ScheduleLog ScheduleLog)
{
db.ScheduleLog.Add(ScheduleLog);
db.SaveChanges();
return ScheduleLog;
}
public ScheduleLog UpdateScheduleLog(ScheduleLog ScheduleLog)
{
db.Entry(ScheduleLog).State = EntityState.Modified;
db.SaveChanges();
return ScheduleLog;
}
public ScheduleLog GetScheduleLog(int ScheduleLogId)
{
return db.ScheduleLog.Find(ScheduleLogId);
}
public void DeleteScheduleLog(int ScheduleLogId)
{
ScheduleLog schedulelog = db.ScheduleLog.Find(ScheduleLogId);
db.ScheduleLog.Remove(schedulelog);
db.SaveChanges();
}
}
}

View File

@ -1,49 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Oqtane.Models;
using Microsoft.EntityFrameworkCore;
using System;
namespace Oqtane.Repository
{
public class ScheduleRepository : IScheduleRepository
{
private MasterDBContext db;
public ScheduleRepository(MasterDBContext context)
{
db = context;
}
public IEnumerable<Schedule> GetSchedules()
{
return db.Schedule.ToList();
}
public Schedule AddSchedule(Schedule Schedule)
{
db.Schedule.Add(Schedule);
db.SaveChanges();
return Schedule;
}
public Schedule UpdateSchedule(Schedule Schedule)
{
db.Entry(Schedule).State = EntityState.Modified;
db.SaveChanges();
return Schedule;
}
public Schedule GetSchedule(int ScheduleId)
{
return db.Schedule.Find(ScheduleId);
}
public void DeleteSchedule(int ScheduleId)
{
Schedule schedule = db.Schedule.Find(ScheduleId);
db.Schedule.Remove(schedule);
db.SaveChanges();
}
}
}

View File

@ -92,6 +92,9 @@ namespace Oqtane.Repository
SiteTemplate.Add(new PageTemplate { Name = "Theme Management", Parent = "Admin", Path = "admin/themes", Icon = "brush", IsNavigation = false, IsPersonalizable = false, EditMode = true, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List<PageTemplateModule> {
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Themes, Oqtane.Client", Title = "Theme Management", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "" }
}});
SiteTemplate.Add(new PageTemplate { Name = "Scheduled Jobs", Parent = "Admin", Path = "admin/jobs", Icon = "timer", IsNavigation = false, IsPersonalizable = false, EditMode = true, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List<PageTemplateModule> {
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Jobs, Oqtane.Client", Title = "Scheduled Jobs", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "" }
}});
SiteTemplate.Add(new PageTemplate { Name = "Upgrade Service", Parent = "Admin", Path = "admin/upgrade", Icon = "aperture", IsNavigation = false, IsPersonalizable = false, EditMode = true, PagePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", PageTemplateModules = new List<PageTemplateModule> {
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.Admin.Upgrade, Oqtane.Client", Title = "Upgrade Service", Pane = "Content", ModulePermissions = "[{\"PermissionName\":\"View\",\"Permissions\":\"Administrators\"},{\"PermissionName\":\"Edit\",\"Permissions\":\"Administrators\"}]", Content = "" }
}});

View File

@ -3,6 +3,7 @@ using System.Linq;
using Oqtane.Models;
using Microsoft.AspNetCore.Http;
using System;
using Oqtane.Shared;
namespace Oqtane.Repository
{
@ -12,12 +13,14 @@ namespace Oqtane.Repository
private readonly string aliasname;
private readonly IAliasRepository Aliases;
private readonly ITenantRepository Tenants;
private readonly SiteState sitestate;
public TenantResolver(MasterDBContext context, IHttpContextAccessor accessor, IAliasRepository Aliases, ITenantRepository Tenants)
public TenantResolver(MasterDBContext context, IHttpContextAccessor accessor, IAliasRepository Aliases, ITenantRepository Tenants, SiteState sitestate)
{
db = context;
this.Aliases = Aliases;
this.Tenants = Tenants;
this.sitestate = sitestate;
aliasname = "";
// get alias based on request context
@ -35,6 +38,13 @@ namespace Oqtane.Repository
aliasname = aliasname.Substring(0, aliasname.Length - 1);
}
}
else // background processes can pass in an alias using the SiteState service
{
if (sitestate != null)
{
aliasname = sitestate.Alias.Name;
}
}
}
public Alias GetAlias()

View File

@ -3,22 +3,6 @@
Create tables
*/
CREATE TABLE [dbo].[Alias](
[AliasId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](200) NOT NULL,
[TenantId] [int] NOT NULL,
[SiteId] [int] NOT NULL,
[CreatedBy] [nvarchar](256) NOT NULL,
[CreatedOn] [datetime] NOT NULL,
[ModifiedBy] [nvarchar](256) NOT NULL,
[ModifiedOn] [datetime] NOT NULL,
CONSTRAINT [PK_Alias] PRIMARY KEY CLUSTERED
(
[AliasId] ASC
)
)
GO
CREATE TABLE [dbo].[Tenant](
[TenantId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](100) NOT NULL,
@ -36,6 +20,23 @@ CREATE TABLE [dbo].[Tenant](
)
GO
CREATE TABLE [dbo].[Alias](
[AliasId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](200) NOT NULL,
[TenantId] [int] NOT NULL,
[SiteId] [int] NOT NULL,
[CreatedBy] [nvarchar](256) NOT NULL,
[CreatedOn] [datetime] NOT NULL,
[ModifiedBy] [nvarchar](256) NOT NULL,
[ModifiedOn] [datetime] NOT NULL,
CONSTRAINT [PK_Alias] PRIMARY KEY CLUSTERED
(
[AliasId] ASC
)
)
GO
CREATE TABLE [dbo].[ModuleDefinition](
[ModuleDefinitionId] [int] IDENTITY(1,1) NOT NULL,
[ModuleDefinitionName] [nvarchar](200) NOT NULL,
@ -50,38 +51,40 @@ CREATE TABLE [dbo].[ModuleDefinition](
)
GO
CREATE TABLE [dbo].[Schedule] (
[ScheduleId] [int] IDENTITY(1,1) NOT NULL,
CREATE TABLE [dbo].[Job] (
[JobId] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](200) NOT NULL,
[JobType] [nvarchar](200) NOT NULL,
[Period] [int] NOT NULL,
[Frequency] [char](1) NOT NULL,
[Interval] [int] NOT NULL,
[StartDate] [datetime] NULL,
[IsActive] [bit] NOT NULL,
[EndDate] [datetime] NULL,
[IsEnabled] [bit] NOT NULL,
[IsStarted] [bit] NOT NULL,
[IsExecuting] [bit] NOT NULL,
[NextExecution] [datetime] NULL,
[RetentionHistory] [int] NOT NULL,
[CreatedBy] [nvarchar](256) NULL,
[CreatedOn] [datetime] NULL,
[ModifiedBy] [nvarchar](256) NULL,
[ModifiedOn] [datetime] NULL,
CONSTRAINT [PK_Schedule] PRIMARY KEY CLUSTERED
[CreatedBy] [nvarchar](256) NOT NULL,
[CreatedOn] [datetime] NOT NULL,
[ModifiedBy] [nvarchar](256) NOT NULL,
[ModifiedOn] [datetime] NOT NULL,
CONSTRAINT [PK_Job] PRIMARY KEY CLUSTERED
(
[ScheduleId] ASC
[JobId] ASC
)
)
GO
CREATE TABLE [dbo].[ScheduleLog] (
[ScheduleLogId] [int] IDENTITY(1,1) NOT NULL,
[ScheduleId] [int] NOT NULL,
CREATE TABLE [dbo].[JobLog] (
[JobLogId] [int] IDENTITY(1,1) NOT NULL,
[JobId] [int] NOT NULL,
[StartDate] [datetime] NOT NULL,
[FinishDate] [datetime] NULL,
[Succeeded] [bit] NULL,
[Notes] [nvarchar](max) NULL,
CONSTRAINT [PK_ScheduleLog] PRIMARY KEY CLUSTERED
CONSTRAINT [PK_JobLog] PRIMARY KEY CLUSTERED
(
[ScheduleLogId] ASC
[JobLogId] ASC
)
)
GO
@ -107,8 +110,8 @@ REFERENCES [dbo].[Tenant] ([TenantId])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[ScheduleLog] WITH NOCHECK ADD CONSTRAINT [FK_ScheduleLog_Schedule] FOREIGN KEY([ScheduleId])
REFERENCES [dbo].[Schedule] ([ScheduleId])
ALTER TABLE [dbo].[JobLog] WITH NOCHECK ADD CONSTRAINT [FK_JobLog_Job] FOREIGN KEY([JobId])
REFERENCES [dbo].[Job] ([JobId])
ON DELETE CASCADE
GO
@ -132,3 +135,12 @@ VALUES (1, N'{Alias}', 1, 1, '', getdate(), '', getdate())
GO
SET IDENTITY_INSERT [dbo].[Alias] OFF
GO
SET IDENTITY_INSERT [dbo].[Job] ON
GO
INSERT [dbo].[Job] ([JobId], [Name], [JobType], [Frequency], [Interval], [StartDate], [EndDate], [IsEnabled], [IsStarted], [IsExecuting], [NextExecution], [RetentionHistory], [CreatedBy], [CreatedOn], [ModifiedBy], [ModifiedOn])
VALUES (1, N'Sample Daily Job', N'Oqtane.Infrastructure.SampleJob, Oqtane.Server', N'd', 1, null, null, 1, 0, 0, null, 10, '', getdate(), '', getdate())
GO
SET IDENTITY_INSERT [dbo].[Job] OFF
GO

View File

@ -100,8 +100,8 @@ namespace Oqtane.Server
services.AddScoped<IFileService, FileService>();
services.AddScoped<IPackageService, PackageService>();
services.AddScoped<ILogService, LogService>();
services.AddScoped<IScheduleService, ScheduleService>();
services.AddScoped<IScheduleLogService, ScheduleLogService>();
services.AddScoped<IJobService, JobService>();
services.AddScoped<IJobLogService, JobLogService>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
@ -173,8 +173,8 @@ namespace Oqtane.Server
services.AddTransient<ISettingRepository, SettingRepository>();
services.AddTransient<ILogRepository, LogRepository>();
services.AddTransient<ILogManager, LogManager>();
services.AddTransient<IScheduleRepository, ScheduleRepository>();
services.AddTransient<IScheduleLogRepository, ScheduleLogRepository>();
services.AddTransient<IJobRepository, JobRepository>();
services.AddTransient<IJobLogRepository, JobLogRepository>();
// get list of loaded assemblies
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
@ -382,8 +382,8 @@ namespace Oqtane.Server
services.AddTransient<ISettingRepository, SettingRepository>();
services.AddTransient<ILogRepository, LogRepository>();
services.AddTransient<ILogManager, LogManager>();
services.AddTransient<IScheduleRepository, ScheduleRepository>();
services.AddTransient<IScheduleLogRepository, ScheduleLogRepository>();
services.AddTransient<IJobRepository, JobRepository>();
services.AddTransient<IJobLogRepository, JobLogRepository>();
// get list of loaded assemblies
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();