WIP separation Web et API

This commit is contained in:
Paul Schneider 2025-02-12 20:41:14 +00:00
commit 49826eae4a
55 changed files with 68 additions and 95 deletions

View file

@ -0,0 +1,151 @@
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Yavsc.Helpers;
using Yavsc.Models;
using Yavsc.Models.Blog;
namespace Yavsc.Controllers
{
[Produces("application/json")]
[Route("api/blog")]
public class BlogApiController : Controller
{
private readonly ApplicationDbContext _context;
public BlogApiController(ApplicationDbContext context)
{
_context = context;
}
// GET: api/BlogApi
[HttpGet]
public IEnumerable<BlogPost> GetBlogspot()
{
return _context.BlogSpot.Where(b => b.Visible).OrderByDescending(b => b.UserModified);
}
// GET: api/BlogApi/5
[HttpGet("{id}", Name = "GetBlog")]
public IActionResult GetBlog([FromRoute] long id)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
BlogPost blog = _context.BlogSpot.Single(m => m.Id == id);
if (blog == null)
{
return NotFound();
}
return Ok(blog);
}
// PUT: api/BlogApi/5
[HttpPut("{id}")]
public IActionResult PutBlog(long id, [FromBody] BlogPost blog)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != blog.Id)
{
return BadRequest();
}
_context.Entry(blog).State = EntityState.Modified;
try
{
_context.SaveChanges(User.GetUserId());
}
catch (DbUpdateConcurrencyException)
{
if (!BlogExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return new StatusCodeResult(StatusCodes.Status204NoContent);
}
// POST: api/BlogApi
[HttpPost]
public IActionResult PostBlog([FromBody] Models.Blog.BlogPost blog)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_context.BlogSpot.Add(blog);
try
{
_context.SaveChanges(User.GetUserId());
}
catch (DbUpdateException)
{
if (BlogExists(blog.Id))
{
return new StatusCodeResult(StatusCodes.Status409Conflict);
}
else
{
throw;
}
}
return CreatedAtRoute("GetBlog", new { id = blog.Id }, blog);
}
// DELETE: api/BlogApi/5
[HttpDelete("{id}")]
public IActionResult DeleteBlog(long id)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
BlogPost blog = _context.BlogSpot.Single(m => m.Id == id);
if (blog == null)
{
return NotFound();
}
_context.BlogSpot.Remove(blog);
_context.SaveChanges(User.GetUserId());
return Ok(blog);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_context.Dispose();
}
base.Dispose(disposing);
}
private bool BlogExists(long id)
{
return _context.BlogSpot.Count(e => e.Id == id) > 0;
}
}
}

View file

@ -0,0 +1,147 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Yavsc.Models;
using Yavsc.Models.Blog;
namespace Yavsc.Controllers
{
[Produces("application/json")]
[Route("api/blogtags")]
public class BlogTagsApiController : Controller
{
private readonly ApplicationDbContext _context;
public BlogTagsApiController(ApplicationDbContext context)
{
_context = context;
}
// GET: api/BlogTagsApi
[HttpGet]
public IEnumerable<BlogTag> GetTagsDomain()
{
return _context.TagsDomain;
}
// GET: api/BlogTagsApi/5
[HttpGet("{id}", Name = "GetBlogTag")]
public async Task<IActionResult> GetBlogTag([FromRoute] long id)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
BlogTag blogTag = await _context.TagsDomain.SingleAsync(m => m.PostId == id);
if (blogTag == null)
{
return NotFound();
}
return Ok(blogTag);
}
// PUT: api/BlogTagsApi/5
[HttpPut("{id}")]
public async Task<IActionResult> PutBlogTag([FromRoute] long id, [FromBody] BlogTag blogTag)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != blogTag.PostId)
{
return BadRequest();
}
_context.Entry(blogTag).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!BlogTagExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return new StatusCodeResult(StatusCodes.Status204NoContent);
}
// POST: api/BlogTagsApi
[HttpPost]
public async Task<IActionResult> PostBlogTag([FromBody] BlogTag blogTag)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_context.TagsDomain.Add(blogTag);
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateException)
{
if (BlogTagExists(blogTag.PostId))
{
return new StatusCodeResult(StatusCodes.Status409Conflict);
}
else
{
throw;
}
}
return CreatedAtRoute("GetBlogTag", new { id = blogTag.PostId }, blogTag);
}
// DELETE: api/BlogTagsApi/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteBlogTag([FromRoute] long id)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
BlogTag blogTag = await _context.TagsDomain.SingleAsync(m => m.PostId == id);
if (blogTag == null)
{
return NotFound();
}
_context.TagsDomain.Remove(blogTag);
await _context.SaveChangesAsync();
return Ok(blogTag);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_context.Dispose();
}
base.Dispose(disposing);
}
private bool BlogTagExists(long id)
{
return _context.TagsDomain.Count(e => e.PostId == id) > 0;
}
}
}

View file

@ -0,0 +1,135 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Yavsc.Helpers;
using Yavsc.Models;
using Yavsc.Models.Blog;
namespace Yavsc.Controllers
{
[Authorize]
[Produces("application/json")]
[Route("api/blogcomments")]
public class CommentsApiController : Controller
{
private readonly ApplicationDbContext _context;
public CommentsApiController(ApplicationDbContext context)
{
_context = context;
}
[HttpGet("{id}", Name = "GetComment")]
public async Task<IActionResult> GetComment([FromRoute] long id)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
Comment comment = await _context.Comment.SingleAsync(m => m.Id == id);
if (comment == null)
{
return NotFound();
}
return Ok(comment);
}
[HttpPost]
public async Task<IActionResult> Post([FromBody] CommentPost post)
{
if (!ModelState.IsValid)
{
return new BadRequestObjectResult(ModelState);
}
var article = await _context.BlogSpot.FirstOrDefaultAsync
(p=> p.Id == post.ReceiverId);
if (article==null) {
ModelState.AddModelError("ReceiverId", "not found");
return BadRequest(ModelState);
}
if (post.ParentId!=null)
{
var parentExists = _context.Comment.Any(c => c.Id == post.ParentId);
if (!parentExists)
{
ModelState.AddModelError("ParentId", "not found");
return BadRequest(ModelState);
}
}
string uid = User.GetUserId();
Comment c = new Comment{
ReceiverId = post.ReceiverId,
Content = post.Content,
ParentId = post.ParentId,
AuthorId = uid,
UserModified = uid
};
_context.Comment.Add(c);
try
{
await _context.SaveChangesAsync(uid);
}
catch (DbUpdateException)
{
if (CommentExists(c.Id))
{
return new StatusCodeResult(StatusCodes.Status409Conflict);
}
else
{
throw;
}
}
return CreatedAtRoute("GetComment", new { id = c.Id }, new { id = c.Id, dateCreated = c.DateCreated });
}
// DELETE: api/CommentsApi/5
[HttpDelete("{id}")]
public async Task<IActionResult> Delete([FromRoute] long id)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
Comment comment = await _context.Comment.SingleAsync(m => m.Id == id);
if (comment == null)
{
return NotFound();
}
RemoveRecursive(comment);
await _context.SaveChangesAsync(User.GetUserId());
return Ok(comment);
}
private void RemoveRecursive (Comment comment)
{
var children = _context.Comment.Where
(c=>c.ParentId==comment.Id).ToList();
foreach (var child in children) {
RemoveRecursive(child);
}
_context.Comment.Remove(comment);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_context.Dispose();
}
base.Dispose(disposing);
}
private bool CommentExists(long id)
{
return _context.Comment.Count(e => e.Id == id) > 0;
}
}
}

View file

@ -0,0 +1,185 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Yavsc.Models;
namespace Yavsc.ApiControllers
{
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Yavsc.Helpers;
using Yavsc.Exceptions;
using Yavsc.Models.FileSystem;
using System.ComponentModel.DataAnnotations;
using Yavsc.Attributes.Validation;
using System.IO;
[Authorize,Route("api/fs")]
public partial class FileSystemApiController : Controller
{
readonly ApplicationDbContext dbContext;
private readonly IAuthorizationService AuthorizationService;
private readonly ILogger _logger;
public FileSystemApiController(ApplicationDbContext context,
IAuthorizationService authorizationService,
ILoggerFactory loggerFactory)
{
AuthorizationService = authorizationService;
dbContext = context;
_logger = loggerFactory.CreateLogger<FileSystemApiController>();
}
[HttpGet()]
public IActionResult Get()
{
return GetDir(null);
}
[HttpGet("{*subdir}")]
public IActionResult GetDir([ValidRemoteUserFilePath] string subdir="")
{
if (!ModelState.IsValid) return new BadRequestObjectResult(ModelState);
// _logger.LogInformation($"listing files from {User.Identity.Name}{subdir}");
var files = AbstractFileSystemHelpers.GetUserFiles(User.GetUserId(), subdir);
return Ok(files);
}
[HttpPost("{*subdir}")]
public IActionResult Post([ValidRemoteUserFilePath] string subdir="")
{
if (!ModelState.IsValid) return new BadRequestObjectResult(ModelState);
string destDir = null;
List<FileRecievedInfo> received = new List<FileRecievedInfo>();
InvalidPathException pathex = null;
try {
destDir = User.InitPostToFileSystem(subdir);
} catch (InvalidPathException ex) {
pathex = ex;
}
if (pathex!=null) {
_logger.LogError($"invalid sub path: '{subdir}'.");
return BadRequest(pathex);
}
_logger.LogInformation($"Receiving files, saved in '{destDir}' (specified as '{subdir}').");
var uid = User.GetUserId();
var user = dbContext.Users.Single(
u => u.Id == uid
);
int i=0;
_logger.LogInformation($"Receiving {Request.Form.Files.Count} files.");
foreach (var f in Request.Form.Files)
{
var item = user.ReceiveUserFile(destDir, f);
dbContext.SaveChanges(User.GetUserId());
received.Add(item);
_logger.LogInformation($"Received '{item.FileName}'.");
if (item.QuotaOffensed)
break;
i++;
};
return Ok(received);
}
[Route("/api/fsc/addquota/{uname}/{len}")]
[Authorize("AdministratorOnly")]
public IActionResult AddQuota(string uname, int len)
{
var uid = User.FindFirstValue(ClaimTypes.NameIdentifier);
var user = dbContext.Users.FirstOrDefault(
u => u.UserName == uname
);
if (user==null) return new BadRequestObjectResult(new { error = "no such use" });
user.AddQuota(len);
dbContext.SaveChanges(uid);
return Ok(len);
}
[HttpPost]
[Route("/api/fsc/mvftd")]
[Authorize()]
public IActionResult MoveFile([FromBody] RenameFileQuery query)
{
if (!ModelState.IsValid) return new BadRequestObjectResult(ModelState);
var uid = User.FindFirstValue(ClaimTypes.NameIdentifier);
var user = dbContext.Users.Single(
u => u.Id == uid
);
var info = user.MoveUserFileToDir(query.id, query.to);
if (!info.Done) return new BadRequestObjectResult(info);
return Ok(new { moved = query.id });
}
[HttpPost]
[Route("/api/fsc/mvf")]
[Authorize()]
public IActionResult RenameFile([FromBody] RenameFileQuery query)
{
if (!ModelState.IsValid) {
var idvr = new ValidRemoteUserFilePathAttribute();
return this.BadRequest(new { id = idvr.IsValid(query.id), to = idvr.IsValid(query.to), errors = ModelState });
}
_logger.LogInformation($"Valid move query: {query.id} => {query.to}");
var uid = User.FindFirstValue(ClaimTypes.NameIdentifier);
var user = dbContext.Users.Single(
u => u.Id == uid
);
try {
if (Config.UserFilesOptions.FileProvider.GetFileInfo(Path.Combine(user.UserName, query.id)).Exists)
{
var result = user.MoveUserFile(query.id, query.to);
if (!result.Done) return new BadRequestObjectResult(result);
}
else {
var result = user.MoveUserDir(query.id, query.to);
if (!result.Done) return new BadRequestObjectResult(result);
}
}
catch (Exception ex)
{
return new BadRequestObjectResult(
new FsOperationInfo {
Done = false,
ErrorCode = ErrorCode.InternalError,
ErrorMessage = ex.Message
});
}
return Ok();
}
[HttpDelete("{*id}")]
public IActionResult RemoveDirOrFile ([ValidRemoteUserFilePath] string id)
{
if (!ModelState.IsValid) return new BadRequestObjectResult(ModelState);
var user = dbContext.Users.Single(
u => u.Id == User.GetUserId()
);
try {
var result = user.DeleteUserDirOrFile(id);
if (!result.Done)
return new BadRequestObjectResult(result);
}
catch (Exception ex)
{
return new BadRequestObjectResult(
new FsOperationInfo {
Done = false,
ErrorCode = ErrorCode.InternalError,
ErrorMessage = ex.Message
});
}
return Ok(new { deleted=id });
}
}
}

View file

@ -0,0 +1,72 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Yavsc.Attributes.Validation;
using Yavsc.Helpers;
using Yavsc.Models;
using Yavsc.Models.Messaging;
using Yavsc.Services;
using Microsoft.AspNetCore.SignalR;
namespace Yavsc.ApiControllers
{
[Authorize, Route("api/stream")]
public partial class FileSystemStreamController : Controller
{
private readonly ILogger logger;
private readonly ILiveProcessor liveProcessor;
private readonly IHubContext<ChatHub> hubContext;
readonly ApplicationDbContext dbContext;
public FileSystemStreamController(ApplicationDbContext context, ILiveProcessor liveProcessor, ILoggerFactory loggerFactory,
IHubContext<ChatHub> hubContext)
{
this.dbContext = context;
this.logger = loggerFactory.CreateLogger<FileSystemStreamController>();
this.liveProcessor = liveProcessor;
this.hubContext = hubContext;
}
[Authorize, Route("put/{filename}")]
public async Task<IActionResult> Put([ValidRemoteUserFilePath] string filename)
{
logger.LogInformation("Put : " + filename);
if (!HttpContext.WebSockets.IsWebSocketRequest)
return BadRequest("not a web socket");
if (!HttpContext.User.Identity.IsAuthenticated)
return new UnauthorizedResult();
var subdirs = filename.Split('/');
var filePath = subdirs.Length > 1 ? string.Join("/", subdirs.Take(subdirs.Length-1)) : null;
var shortFileName = subdirs[subdirs.Length-1];
if (!shortFileName.IsValidShortFileName())
{
logger.LogInformation("invalid file name : " + filename);
return BadRequest("invalid file name");
}
logger.LogInformation("validated: api/stream/Put: "+filename);
var userName = User.GetUserName();
string url = string.Format(
"{0}/{1}/{2}",
Config.UserFilesOptions.RequestPath.ToUriComponent(),
userName,
filename
);
hubContext.Clients.All.SendAsync("addPublicStream", new PublicStreamInfo
{
sender = userName,
url = url,
}, $"{userName} is starting a stream!");
string destDir = HttpContext.User.InitPostToFileSystem(filePath);
logger.LogInformation($"Saving flow to {destDir}");
var userId = User.GetUserId();
var user = await dbContext.Users.FirstAsync(u => u.Id == userId);
logger.LogInformation("Accepting stream ...");
await liveProcessor.AcceptStream(HttpContext, user, destDir, shortFileName);
return Ok();
}
}
}

View file

@ -0,0 +1,23 @@
using Yavsc.Attributes.Validation;
namespace Yavsc.Models.FileSystem
{
public class RenameFileQuery {
[ValidRemoteUserFilePath]
[YaStringLength(1, 512)]
public string id { get; set; }
[YaStringLength(0, 512)]
[ValidRemoteUserFilePath]
public string to { get; set; }
}
public class MoveFileQuery {
[ValidRemoteUserFilePath]
[YaStringLength(1, 512)]
public string id { get; set; }
[YaStringLength(0, 512)]
[ValidRemoteUserFilePath]
public string to { get; set; }
}
}

View file

@ -0,0 +1,150 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Yavsc.Controllers
{
using System.Security.Claims;
using Microsoft.EntityFrameworkCore;
using Models;
using Yavsc.Helpers;
using Yavsc.Models.Blog;
[Produces("application/json")]
[Route("~/api/PostTagsApi")]
public class PostTagsApiController : Controller
{
private ApplicationDbContext _context;
public PostTagsApiController(ApplicationDbContext context)
{
_context = context;
}
// GET: api/PostTagsApi
[HttpGet]
public IEnumerable<BlogTag> GetTagsDomain()
{
return _context.TagsDomain;
}
// GET: api/PostTagsApi/5
[HttpGet("{id}", Name = "GetPostTag")]
public IActionResult GetPostTag([FromRoute] long id)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
BlogTag postTag = _context.TagsDomain.Single(m => m.PostId == id);
if (postTag == null)
{
return NotFound();
}
return Ok(postTag);
}
// PUT: api/PostTagsApi/5
[HttpPut("{id}")]
public IActionResult PutPostTag(long id, [FromBody] BlogTag postTag)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != postTag.PostId)
{
return BadRequest();
}
_context.Entry(postTag).State = EntityState.Modified;
try
{
_context.SaveChanges(User.GetUserId());
}
catch (DbUpdateConcurrencyException)
{
if (!PostTagExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return new StatusCodeResult(StatusCodes.Status204NoContent);
}
// POST: api/PostTagsApi
[HttpPost]
public IActionResult PostPostTag([FromBody] BlogTag postTag)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_context.TagsDomain.Add(postTag);
try
{
_context.SaveChanges(User.GetUserId());
}
catch (DbUpdateException)
{
if (PostTagExists(postTag.PostId))
{
return new StatusCodeResult(StatusCodes.Status409Conflict);
}
else
{
throw;
}
}
return CreatedAtRoute("GetPostTag", new { id = postTag.PostId }, postTag);
}
// DELETE: api/PostTagsApi/5
[HttpDelete("{id}")]
public IActionResult DeletePostTag(long id)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
BlogTag postTag = _context.TagsDomain.Single(m => m.PostId == id);
if (postTag == null)
{
return NotFound();
}
_context.TagsDomain.Remove(postTag);
_context.SaveChanges(User.GetUserId());
return Ok(postTag);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_context.Dispose();
}
base.Dispose(disposing);
}
private bool PostTagExists(long id)
{
return _context.TagsDomain.Count(e => e.PostId == id) > 0;
}
}
}

View file

@ -0,0 +1,152 @@
using Microsoft.AspNetCore.Mvc;
using Yavsc.Models;
namespace Yavsc.Controllers
{
using System.Security.Claims;
using Microsoft.EntityFrameworkCore;
using Models.Relationship;
using Yavsc.Helpers;
[Produces("application/json")]
[Route("api/TagsApi")]
public class TagsApiController : Controller
{
private ApplicationDbContext _context;
ILogger _logger;
public TagsApiController(ApplicationDbContext context,
ILoggerFactory loggerFactory)
{
_context = context;
_logger = loggerFactory.CreateLogger<TagsApiController>();
}
// GET: api/TagsApi
[HttpGet]
public IEnumerable<Tag> GetTag()
{
return _context.Tags;
}
// GET: api/TagsApi/5
[HttpGet("{id}", Name = "GetTag")]
public IActionResult GetTag([FromRoute] long id)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
Tag tag = _context.Tags.Single(m => m.Id == id);
if (tag == null)
{
return NotFound();
}
return Ok(tag);
}
// PUT: api/TagsApi/5
[HttpPut("{id}")]
public IActionResult PutTag(long id, [FromBody] Tag tag)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != tag.Id)
{
return BadRequest();
}
_context.Entry(tag).State = EntityState.Modified;
try
{
_context.SaveChanges(User.GetUserId());
_logger.LogInformation("Tag created");
}
catch (DbUpdateConcurrencyException)
{
if (!TagExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return new StatusCodeResult(StatusCodes.Status204NoContent);
}
// POST: api/TagsApi
[HttpPost]
public IActionResult PostTag([FromBody] Tag tag)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_context.Tags.Add(tag);
try
{
_context.SaveChanges(User.GetUserId());
}
catch (DbUpdateException)
{
if (TagExists(tag.Id))
{
return new StatusCodeResult(StatusCodes.Status409Conflict);
}
else
{
throw;
}
}
return CreatedAtRoute("GetTag", new { id = tag.Id }, tag);
}
// DELETE: api/TagsApi/5
[HttpDelete("{id}")]
public IActionResult DeleteTag(long id)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
Tag tag = _context.Tags.Single(m => m.Id == id);
if (tag == null)
{
return NotFound();
}
_context.Tags.Remove(tag);
_context.SaveChanges(User.GetUserId());
return Ok(tag);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_context.Dispose();
}
base.Dispose(disposing);
}
private bool TagExists(long id)
{
return _context.Tags.Count(e => e.Id == id) > 0;
}
}
}

View file

@ -0,0 +1,15 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Yavsc.ApiControllers
{
using System.ComponentModel.DataAnnotations;
using Yavsc.Attributes.Validation;
[Authorize,Route("~/api/test")]
public class TestApiController : Controller
{
}
}