Files
Disco/Disco.Services/Interop/DiscoServices/Upload/UploadOnlineSyncTask.cs
T
2025-06-15 17:56:13 +10:00

210 lines
10 KiB
C#

using Disco.Data.Repository;
using Disco.Models.Repository;
using Disco.Models.Services.Interop.DiscoServices;
using Disco.Services.Tasks;
using Disco.Services.Users;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using Quartz;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
namespace Disco.Services.Interop.DiscoServices.Upload
{
public class UploadOnlineSyncTask : ScheduledTask
{
private readonly static object runSerialLock = new object();
private const int connectNotificationType = 1628986937;
private const string AttachmentHandlerId = "Upload";
public override string TaskName { get { return "Upload Online - Sync"; } }
public override bool SingleInstanceTask { get; } = false;
public static ScheduledTaskStatus RunningStatus
=> ScheduledTasks.GetTaskStatuses(typeof(UploadOnlineSyncTask)).Where(ts => ts.IsRunning).FirstOrDefault();
public override void InitalizeScheduledTask(DiscoDataContext Database)
{
OnlineServicesConnect.SubscribeToNotifications(HandleConnectNotification, connectNotificationType);
}
public static ScheduledTaskStatus ScheduleInOneHour()
{
var instance = new UploadOnlineSyncTask();
var trigger = TriggerBuilder.Create()
.StartAt(DateTimeOffset.Now.AddHours(1));
return instance.ScheduleTask(trigger);
}
private static ScheduledTaskStatus ScheduleNow(string hintFileId)
{
var taskState = new JobDataMap() { { "hintFileId", hintFileId } };
var instance = new UploadOnlineSyncTask();
return instance.ScheduleTask(taskState);
}
private static void HandleConnectNotification(IConnectNotification notification)
{
if (notification.Version == 1 && notification.Type == connectNotificationType && Guid.TryParse(notification.Content, out _))
{
ScheduleNow(notification.Content);
}
}
protected override void ExecuteTask()
{
var hintFileId = (string)(ExecutionContext.JobDetail?.JobDataMap?["hintFileId"]);
using (var database = new DiscoDataContext())
{
if (hintFileId != null && UploadAttachmentExists(database, hintFileId))
{
Status.Finished("Hinted attachment has already been downloaded");
return;
}
lock (runSerialLock)
{
if (hintFileId != null && UploadAttachmentExists(database, hintFileId))
{
Status.Finished("Hinted attachment has already been downloaded");
return;
}
var lastFileId = LastUploadFileId(database);
Status.UpdateStatus(10, "Fetching attachments from Online Services");
var archiveStream = UploadOnlineService.SyncUploads(lastFileId, hintFileId);
if (archiveStream == null)
{
Status.Finished("No new uploads found");
return;
}
using (var archive = new ZipArchive(archiveStream, ZipArchiveMode.Read))
{
Status.UpdateStatus(45, "Reading manifest");
List<SyncUploadModel> manifest;
var manifestEntry = archive.GetEntry("manifest.json");
using (var manifestStream = manifestEntry.Open())
{
using (var manifestReader = new StreamReader(manifestStream))
{
using (var jsonReader = new JsonTextReader(manifestReader))
{
var serializerSettings = new JsonSerializerSettings()
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
};
var serializer = JsonSerializer.Create(serializerSettings);
manifest = serializer.Deserialize<List<SyncUploadModel>>(jsonReader);
}
}
}
if (manifest == null || manifest.Count == 0)
{
Status.Finished("No uploads found in the archive manifest");
return;
}
Status.UpdateStatus(50, $"Importing {manifest.Count} attachments");
var attachmentStream = new MemoryStream();
foreach (var upload in manifest)
{
var archiveEntry = archive.GetEntry(upload.Id);
if (archiveEntry == null)
continue;
if (!UserService.TryGetUser(upload.CreatedBy, database, false, out var createdBy))
continue;
var createdOn = DateTimeOffset.FromUnixTimeMilliseconds(upload.CreatedOn).ToLocalTime().DateTime;
using (var uploadStream = archiveEntry.Open())
{
uploadStream.CopyTo(attachmentStream);
attachmentStream.Position = 0;
switch (upload.TargetType)
{
case AttachmentTypes.Device:
var device = database.Devices.Find(upload.TargetId);
if (device == null)
continue;
if (database.DeviceAttachments.Any(da => da.DeviceSerialNumber == device.SerialNumber && da.HandlerId == AttachmentHandlerId && da.HandlerReferenceId == upload.Id))
continue;
device.CreateAttachment(database, createdBy, upload.FileName, createdOn, upload.MimeType, upload.Comments, attachmentStream, DocumentTemplate: null, PdfThumbnail: null, HandlerId: AttachmentHandlerId, HandlerReferenceId: upload.Id, HandlerData: null);
break;
case AttachmentTypes.Job:
var jobId = int.Parse(upload.TargetId);
var job = database.Jobs.Find(jobId);
if (job == null)
continue;
if (database.JobAttachments.Any(ja => ja.JobId == jobId && ja.HandlerId == AttachmentHandlerId && ja.HandlerReferenceId == upload.Id))
continue;
job.CreateAttachment(database, createdBy, upload.FileName, createdOn, upload.MimeType, upload.Comments, attachmentStream, DocumentTemplate: null, PdfThumbnail: null, HandlerId: AttachmentHandlerId, HandlerReferenceId: upload.Id, HandlerData: null);
break;
case AttachmentTypes.User:
if (UserService.TryGetUser(upload.TargetId, database, true, out var targetUser))
{
if (database.UserAttachments.Any(ua => ua.UserId == targetUser.UserId && ua.HandlerId == AttachmentHandlerId && ua.HandlerReferenceId == upload.Id))
continue;
targetUser.CreateAttachment(database, createdBy, upload.FileName, createdOn, upload.MimeType, upload.Comments, attachmentStream, DocumentTemplate: null, PdfThumbnail: null, HandlerId: AttachmentHandlerId, HandlerReferenceId: upload.Id, HandlerData: null);
}
break;
}
attachmentStream.SetLength(0);
}
}
Status.Finished("Sync completed successfully");
}
}
}
}
private static bool UploadAttachmentExists(DiscoDataContext database, string fileId)
{
return database.JobAttachments
.Any(ja => ja.HandlerId == AttachmentHandlerId && ja.HandlerReferenceId == fileId) ||
database.DeviceAttachments
.Any(da => da.HandlerId == AttachmentHandlerId && da.HandlerReferenceId == fileId) ||
database.UserAttachments
.Any(ua => ua.HandlerId == AttachmentHandlerId && ua.HandlerReferenceId == fileId);
}
private static string LastUploadFileId(DiscoDataContext database)
{
var ids = new List<string>(3)
{
database.JobAttachments.Where(ja => ja.HandlerId == AttachmentHandlerId).Max(ja => ja.HandlerReferenceId),
database.DeviceAttachments.Where(ja => ja.HandlerId == AttachmentHandlerId).Max(ja => ja.HandlerReferenceId),
database.UserAttachments.Where(ja => ja.HandlerId == AttachmentHandlerId).Max(ja => ja.HandlerReferenceId),
};
ids.Sort(StringComparer.Ordinal);
if (ids[2] == null)
return null;
else
return ids[2];
}
private class SyncUploadModel
{
public string Id { get; set; }
public string CreatedBy { get; set; }
public long CreatedOn { get; set; }
public AttachmentTypes TargetType { get; set; }
public string TargetId { get; set; }
public string FileName { get; set; }
public string MimeType { get; set; }
public string Comments { get; set; }
}
}
}