v1.0.0: ApiController — uploads, ICS parsing
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NoticeBoard.Data;
|
||||
using Ical.Net;
|
||||
using Ical.Net.CalendarComponents;
|
||||
using Ical.Net.DataTypes;
|
||||
|
||||
namespace NoticeBoard.Controllers;
|
||||
|
||||
public class ApiController : Controller
|
||||
{
|
||||
private readonly IWebHostEnvironment _env;
|
||||
private readonly IConfiguration _config;
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
|
||||
public ApiController(IWebHostEnvironment env, IConfiguration config, IHttpClientFactory httpClientFactory)
|
||||
{
|
||||
_env = env;
|
||||
_config = config;
|
||||
_httpClientFactory = httpClientFactory;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Upload(IFormFile file)
|
||||
{
|
||||
if (file == null || file.Length == 0)
|
||||
return BadRequest(new { error = "No file provided." });
|
||||
|
||||
var maxSize = _config.GetValue<int>("MaxUploadSizeMB", 20) * 1024 * 1024;
|
||||
if (file.Length > maxSize)
|
||||
return BadRequest(new { error = $"File too large. Maximum {maxSize / 1024 / 1024}MB." });
|
||||
|
||||
var allowedExts = new[] { ".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg", ".bmp", ".ico" };
|
||||
var ext = Path.GetExtension(file.FileName).ToLower();
|
||||
if (!allowedExts.Contains(ext))
|
||||
return BadRequest(new { error = "File type not allowed. Use: " + string.Join(", ", allowedExts) });
|
||||
|
||||
var uploadsPath = Path.Combine(_env.WebRootPath, "uploads");
|
||||
if (!Directory.Exists(uploadsPath))
|
||||
Directory.CreateDirectory(uploadsPath);
|
||||
|
||||
var fileName = $"{DateTime.UtcNow:yyyyMMdd_HHmmss}_{Guid.NewGuid():N}{ext}";
|
||||
var filePath = Path.Combine(uploadsPath, fileName);
|
||||
|
||||
using (var stream = new FileStream(filePath, FileMode.Create))
|
||||
{
|
||||
await file.CopyToAsync(stream);
|
||||
}
|
||||
|
||||
return Json(new { location = $"/uploads/{fileName}" });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> UploadFile(IFormFile file)
|
||||
{
|
||||
if (file == null || file.Length == 0)
|
||||
return BadRequest(new { error = "No file provided." });
|
||||
|
||||
var uploadsPath = Path.Combine(_env.WebRootPath, "uploads");
|
||||
if (!Directory.Exists(uploadsPath))
|
||||
Directory.CreateDirectory(uploadsPath);
|
||||
|
||||
var ext = Path.GetExtension(file.FileName).ToLower();
|
||||
var fileName = $"{DateTime.UtcNow:yyyyMMdd_HHmmss}_{Guid.NewGuid():N}{ext}";
|
||||
var filePath = Path.Combine(uploadsPath, fileName);
|
||||
|
||||
using (var stream = new FileStream(filePath, FileMode.Create))
|
||||
{
|
||||
await file.CopyToAsync(stream);
|
||||
}
|
||||
|
||||
return Json(new { url = $"/uploads/{fileName}", name = file.FileName, size = file.Length });
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public IActionResult ListUploads()
|
||||
{
|
||||
var uploadsPath = Path.Combine(_env.WebRootPath, "uploads");
|
||||
if (!Directory.Exists(uploadsPath))
|
||||
return Json(new { files = Array.Empty<object>() });
|
||||
|
||||
var files = Directory.GetFiles(uploadsPath)
|
||||
.Select(f => new FileInfo(f))
|
||||
.OrderByDescending(f => f.CreationTimeUtc)
|
||||
.Select(f => new
|
||||
{
|
||||
name = f.Name,
|
||||
url = $"/uploads/{f.Name}",
|
||||
size = f.Length,
|
||||
created = f.CreationTimeUtc
|
||||
});
|
||||
|
||||
return Json(new { files });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public IActionResult DeleteUpload([FromBody] DeleteFileRequest request)
|
||||
{
|
||||
if (string.IsNullOrEmpty(request?.FileName))
|
||||
return BadRequest();
|
||||
|
||||
var filePath = Path.Combine(_env.WebRootPath, "uploads", Path.GetFileName(request.FileName));
|
||||
if (System.IO.File.Exists(filePath))
|
||||
{
|
||||
System.IO.File.Delete(filePath);
|
||||
return Ok();
|
||||
}
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> ParseIcs(string url)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(url))
|
||||
return BadRequest(new { error = "No URL provided." });
|
||||
|
||||
try
|
||||
{
|
||||
string icsContent;
|
||||
|
||||
if (url.StartsWith("/uploads/"))
|
||||
{
|
||||
var filePath = Path.Combine(_env.WebRootPath, url.TrimStart('/'));
|
||||
if (!System.IO.File.Exists(filePath))
|
||||
return NotFound(new { error = "ICS file not found." });
|
||||
icsContent = await System.IO.File.ReadAllTextAsync(filePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
var client = _httpClientFactory.CreateClient();
|
||||
client.Timeout = TimeSpan.FromSeconds(15);
|
||||
icsContent = await client.GetStringAsync(url);
|
||||
}
|
||||
|
||||
var calendar = Calendar.Load(icsContent);
|
||||
var now = DateTime.Now;
|
||||
var upcoming = calendar.Events
|
||||
.Where(e => e.DtEnd != null ? e.DtEnd.AsSystemLocal >= now : e.DtStart.AsSystemLocal >= now.AddDays(-1))
|
||||
.OrderBy(e => e.DtStart.AsSystemLocal)
|
||||
.Take(20)
|
||||
.Select(e => new
|
||||
{
|
||||
summary = e.Summary,
|
||||
description = e.Description,
|
||||
location = e.Location,
|
||||
start = e.DtStart?.AsSystemLocal.ToString("yyyy-MM-dd HH:mm"),
|
||||
end = e.DtEnd?.AsSystemLocal.ToString("yyyy-MM-dd HH:mm"),
|
||||
allDay = e.IsAllDay
|
||||
});
|
||||
|
||||
return Json(new { events = upcoming });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(new { error = $"Failed to parse ICS: {ex.Message}" });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class DeleteFileRequest
|
||||
{
|
||||
public string? FileName { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user