using AsbCloudApp.Data; using AsbCloudApp.Services; using AsbCloudDb.Model; using Mapster; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace AsbCloudInfrastructure.Services { public class FileService : IFileService { public string RootPath { get; private set; } private readonly IQueryable dbSetConfigured; private readonly IAsbCloudDbContext db; private readonly IGoogleDriveService googleDriveService; public FileService(IAsbCloudDbContext db, IGoogleDriveService googleDriveService) { RootPath = "files"; this.db = db; dbSetConfigured = db.Files .Include(f => f.Author) .ThenInclude(u => u.Company) .ThenInclude(c => c.CompanyType); this.googleDriveService = googleDriveService; } public async Task GetFileWebUrlAsync(FileInfoDto fileInfo, string userLogin, CancellationToken token = default) { var fileWebUrl = fileInfo.PublishInfo?.WebStorageFileUrl; if (!string.IsNullOrEmpty(fileWebUrl)) return fileWebUrl; var relativePath = GetUrl(fileInfo); var fileWebLink = await PublishFileToCloudAsync(relativePath, fileInfo.Name, token); await SaveWeblinkToFileInfo(fileInfo.Id, userLogin, fileWebLink, token); return fileWebLink; } public async Task MoveAsync(int idWell, int? idUser, int idCategory, string destinationFileName, string srcFilePath, CancellationToken token = default) { destinationFileName = Path.GetFileName(destinationFileName); srcFilePath = Path.GetFullPath(srcFilePath); if (!File.Exists(srcFilePath)) throw new ArgumentException($"file {srcFilePath} doesn't exist", nameof(srcFilePath)); var sysFileInfo = new System.IO.FileInfo(srcFilePath); //save info to db var fileInfo = new AsbCloudDb.Model.FileInfo() { IdWell = idWell, IdAuthor = idUser, IdCategory = idCategory, Name = destinationFileName, UploadDate = DateTime.Now, IsDeleted = false, Size = sysFileInfo.Length, }; var entry = db.Files.Add(fileInfo); await db.SaveChangesAsync(token).ConfigureAwait(false); var fileId = entry.Entity.Id; string filePath = MakeFilePath(idWell, idCategory, destinationFileName, fileId); Directory.CreateDirectory(Path.GetDirectoryName(filePath)); File.Move(srcFilePath, filePath); var dto = entry.Entity.Adapt(); return dto; } public async Task SaveAsync(int idWell, int? idUser, int idCategory, string fileFullName, Stream fileStream, CancellationToken token = default) { //save info to db var fileInfo = new AsbCloudDb.Model.FileInfo() { IdWell = idWell, IdAuthor = idUser, IdCategory = idCategory, Name = Path.GetFileName(fileFullName), UploadDate = DateTime.Now, IsDeleted = false, Size = fileStream?.Length ?? 0 }; var entry = db.Files.Add(fileInfo); await db.SaveChangesAsync(token).ConfigureAwait(false); var fileId = entry.Entity.Id; //save stream to disk string filePath = MakeFilePath(idWell, idCategory, fileFullName, fileId); Directory.CreateDirectory(Path.GetDirectoryName(filePath)); using var newfileStream = new FileStream(filePath, FileMode.Create); await fileStream.CopyToAsync(newfileStream, token).ConfigureAwait(false); await fileStream.CopyToAsync(newfileStream); var dto = entry.Entity.Adapt(); return dto; } private string MakeFilePath(int idWell, int idCategory, string fileFullName, int fileId) { return Path.Combine(RootPath, $"{idWell}", $"{idCategory}", $"{fileId}" + $"{Path.GetExtension(fileFullName)}"); } public async Task> GetInfosByCategoryAsync(int idWell, int idCategory, CancellationToken token = default) { var entities = await dbSetConfigured .Include(f => f.FileMarks) .Where(e => e.IdWell == idWell && e.IdCategory == idCategory && e.IsDeleted == false) .AsNoTracking() .ToListAsync(token) .ConfigureAwait(false); var dtos = entities.Adapt(); return dtos; } public async Task> GetInfosAsync(int idWell, int idCategory, string companyName = default, string fileName = default, DateTime begin = default, DateTime end = default, int skip = 0, int take = 32, CancellationToken token = default) { var query = dbSetConfigured .Where(e => e.IdWell == idWell && e.IdCategory == idCategory); query = query.Where(e => !e.IsDeleted); if (idCategory == 13) query = query.Include(f => f.FileMarks); if (!string.IsNullOrEmpty(companyName)) query = query .Include(file => file.Author) .ThenInclude(a => a.Company) .Where(e => (e.Author == null) || (e.Author.Company == null) || e.Author.Company.Caption.Contains(companyName)); if (!string.IsNullOrEmpty(fileName)) query = query.Where(e => e.Name.ToLower().Contains(fileName.ToLower())); if (begin != default) query = query.Where(e => e.UploadDate >= begin); if (end != default) query = query.Where(e => e.UploadDate <= end); var count = await query.CountAsync(token).ConfigureAwait(false); var result = new PaginationContainer(count) { Skip = skip, Take = take, Count = count, }; if (count <= skip) return result; query = query.OrderBy(e => e.UploadDate); if (skip > 0) query = query.Skip(skip); query = query.Take(take); var entities = await query .Take(take).AsNoTracking().ToListAsync(token) .ConfigureAwait(false); var dtos = entities.Adapt(); result.Items.AddRange(dtos); return result; } public async Task GetInfoAsync(int fileId, CancellationToken token = default) { var entity = await dbSetConfigured .AsNoTracking() .FirstOrDefaultAsync(f => f.Id == fileId, token) .ConfigureAwait(false); if (entity is null) return null; var dto = entity.Adapt(); return dto; } public async Task MarkAsDeletedAsync(int idFile, CancellationToken token = default) { var fileInfo = await db.Files.FirstOrDefaultAsync(f => f.Id == idFile, token).ConfigureAwait(false); if (fileInfo is null) return 0; fileInfo.IsDeleted = true; return await db.SaveChangesAsync(token).ConfigureAwait(false); } public async Task DeleteAsync(int idFile, CancellationToken token) { var fileInfo = await db.Files .FirstOrDefaultAsync(f => f.Id == idFile, token) .ConfigureAwait(false); if (fileInfo is null) return 0; var fileName = GetUrl(fileInfo.Adapt()); if (File.Exists(fileName)) File.Delete(fileName); db.Files.Remove(fileInfo); return await db.SaveChangesAsync(token).ConfigureAwait(false); } public string GetUrl(FileInfoDto fileInfo) => GetUrl(fileInfo.IdWell, fileInfo.IdCategory, fileInfo.Id, Path.GetExtension(fileInfo.Name)); public string GetUrl(int idFile) { var fileInfo = db.Files .FirstOrDefault(f => f.Id == idFile); if (fileInfo is null) return null; return GetUrl(fileInfo.IdWell, fileInfo.IdCategory, fileInfo.Id, Path.GetExtension(fileInfo.Name)); } public string GetUrl(int idWell, int idCategory, int idFile, string dotExtention) => Path.Combine(RootPath, idWell.ToString(), idCategory.ToString(), $"{idFile}{dotExtention}"); public async Task CreateFileMarkAsync(int idMark, int idFile, string comment, int fileChangerId, string fileChangerLogin, CancellationToken token = default) { var fileMark = await db.FileMarks .FirstOrDefaultAsync(t => t.IdFile == idFile && t.IdMark == idMark && t.IdUser == fileChangerId, token) .ConfigureAwait(false); if (fileMark is not null) { var dto = fileMark.Adapt(); dto.UserLogin = fileChangerLogin; return dto; } var newFileMark = new FileMark() { IdMark = idMark, DateCreated = DateTime.Now, IdFile = idFile, IdUser = fileChangerId, Comment = comment }; db.FileMarks.Add(newFileMark); await db.SaveChangesAsync(token); var resultDto = newFileMark.Adapt(); resultDto.UserLogin = fileChangerLogin; return resultDto; } public async Task MarkFileMarkAsDeletedAsync(int idMark, CancellationToken token = default) { var fileMark = await db.FileMarks.FirstOrDefaultAsync(m => m.Id == idMark, token); if (fileMark is null) return 0; fileMark.IsDeleted = true; return await db.SaveChangesAsync(token); } private async Task PublishFileToCloudAsync(string filePath, string originalName, CancellationToken token = default) { await using var fileStream = File.Open(filePath, FileMode.Open); var uploadedFileId = await googleDriveService.UploadFileAsync(fileStream, originalName, "", "uploaded", token) .ConfigureAwait(false); await googleDriveService.CreatePublicPermissionForFileAsync(uploadedFileId, token) .ConfigureAwait(false); var webLink = await googleDriveService.GetFileWebLinkAsync(uploadedFileId, token) .ConfigureAwait(false); return webLink; } private async Task SaveWeblinkToFileInfo(int idFileInfo, string userLogin, string weblink, CancellationToken token = default) { var fileInfo = await db.Files.FirstOrDefaultAsync(f => f.Id == idFileInfo, token) .ConfigureAwait(false); fileInfo.PublishInfo = new FilePublishInfo() { PublisherLogin = userLogin, Date = DateTime.Now, WebStorageFileUrl = weblink }; return await db.SaveChangesAsync(token).ConfigureAwait(false); } } }