a fork of iceshrimp.net but a tweaked frontend to my personal liking. waow
fediverse social-media social iceshrimp fedi
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

[backend/api] Add drive file upload/get-by-id/update endpoints & models

+140 -6
+67 -2
Iceshrimp.Backend/Controllers/DriveController.cs
··· 1 1 using System.Diagnostics.CodeAnalysis; 2 2 using Iceshrimp.Backend.Core.Configuration; 3 3 using Iceshrimp.Backend.Core.Database; 4 + using Iceshrimp.Backend.Core.Middleware; 4 5 using Iceshrimp.Backend.Core.Services; 6 + using Iceshrimp.Shared.Schemas; 5 7 using Microsoft.AspNetCore.Cors; 6 8 using Microsoft.AspNetCore.Mvc; 7 9 using Microsoft.EntityFrameworkCore; ··· 10 12 namespace Iceshrimp.Backend.Controllers; 11 13 12 14 [ApiController] 15 + [Route("/api/iceshrimp/drive")] 13 16 public class DriveController( 14 17 DatabaseContext db, 15 18 ObjectStorageService objectStorage, 16 19 [SuppressMessage("ReSharper", "SuggestBaseTypeForParameterInConstructor")] 17 20 IOptionsSnapshot<Config.StorageSection> options, 18 - ILogger<DriveController> logger 21 + ILogger<DriveController> logger, 22 + DriveService driveSvc 19 23 ) : ControllerBase 20 24 { 21 25 [EnableCors("drive")] 22 26 [HttpGet("/files/{accessKey}")] 23 - public async Task<IActionResult> GetFile(string accessKey) 27 + public async Task<IActionResult> GetFileByAccessKey(string accessKey) 24 28 { 25 29 var file = await db.DriveFiles.FirstOrDefaultAsync(p => p.AccessKey == accessKey || 26 30 p.WebpublicAccessKey == accessKey || ··· 66 70 Response.Headers.XContentTypeOptions = "nosniff"; 67 71 return File(stream, file.Type, true); 68 72 } 73 + } 74 + 75 + [HttpPost] 76 + [Authenticate] 77 + [Authorize] 78 + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(DriveFileResponse))] 79 + public async Task<IActionResult> UploadFile([FromForm] IFormFile file) 80 + { 81 + var user = HttpContext.GetUserOrFail(); 82 + var request = new DriveFileCreationRequest 83 + { 84 + Filename = file.FileName, 85 + MimeType = file.ContentType, 86 + IsSensitive = false 87 + }; 88 + var res = await driveSvc.StoreFile(file.OpenReadStream(), user, request); 89 + return await GetFileById(res.Id); 90 + } 91 + 92 + [HttpGet("{id}")] 93 + [Authenticate] 94 + [Authorize] 95 + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(DriveFileResponse))] 96 + [ProducesResponseType(StatusCodes.Status404NotFound, Type = typeof(ErrorResponse))] 97 + public async Task<IActionResult> GetFileById(string id) 98 + { 99 + var user = HttpContext.GetUserOrFail(); 100 + var file = await db.DriveFiles.FirstOrDefaultAsync(p => p.User == user && p.Id == id); 101 + if (file == null) return NotFound(); 102 + 103 + var res = new DriveFileResponse 104 + { 105 + Id = file.Id, 106 + Url = file.PublicUrl, 107 + ThumbnailUrl = file.PublicThumbnailUrl, 108 + Filename = file.Name, 109 + ContentType = file.Type, 110 + Description = file.Comment, 111 + Sensitive = file.IsSensitive 112 + }; 113 + 114 + return Ok(res); 115 + } 116 + 117 + [HttpPatch("{id}")] 118 + [Authenticate] 119 + [Authorize] 120 + [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(DriveFileResponse))] 121 + [ProducesResponseType(StatusCodes.Status404NotFound, Type = typeof(ErrorResponse))] 122 + public async Task<IActionResult> UpdateFile(string id, UpdateDriveFileRequest request) 123 + { 124 + var user = HttpContext.GetUserOrFail(); 125 + var file = await db.DriveFiles.FirstOrDefaultAsync(p => p.User == user && p.Id == id); 126 + if (file == null) return NotFound(); 127 + 128 + file.Name = request.Filename ?? file.Name; 129 + file.IsSensitive = request.Sensitive ?? file.IsSensitive; 130 + file.Comment = request.Description; 131 + await db.SaveChangesAsync(); 132 + 133 + return await GetFileById(id); 69 134 } 70 135 }
+5 -1
Iceshrimp.Backend/Controllers/NoteController.cs
··· 226 226 throw GracefulException.BadRequest("Renote target is nonexistent or inaccessible") 227 227 : null; 228 228 229 + var attachments = request.MediaIds != null 230 + ? await db.DriveFiles.Where(p => request.MediaIds.Contains(p.Id)).ToListAsync() 231 + : null; 232 + 229 233 var note = await noteSvc.CreateNoteAsync(user, (Note.NoteVisibility)request.Visibility, request.Text, 230 - request.Cw, reply, renote); 234 + request.Cw, reply, renote, attachments); 231 235 232 236 return Ok(await noteRenderer.RenderOne(note, user)); 233 237 }
+17
Iceshrimp.Frontend/Core/ControllerModels/DriveControllerModel.cs
··· 1 + using Iceshrimp.Frontend.Core.Services; 2 + using Iceshrimp.Shared.Schemas; 3 + using Microsoft.AspNetCore.Components.Forms; 4 + 5 + namespace Iceshrimp.Frontend.Core.ControllerModels; 6 + 7 + internal class DriveControllerModel(ApiClient api) 8 + { 9 + public Task<DriveFileResponse> UploadFile(IBrowserFile file) => 10 + api.Call<DriveFileResponse>(HttpMethod.Post, $"/drive", data: file); 11 + 12 + public Task<DriveFileResponse?> GetFile(string id) => 13 + api.CallNullable<DriveFileResponse>(HttpMethod.Get, $"/drive/{id}"); 14 + 15 + public Task<DriveFileResponse?> UpdateFile(string id, UpdateDriveFileRequest request) => 16 + api.CallNullable<DriveFileResponse>(HttpMethod.Patch, $"/drive/{id}", data: request); 17 + }
+24 -2
Iceshrimp.Frontend/Core/Services/ApiClient.cs
··· 5 5 using System.Text.Json; 6 6 using Iceshrimp.Frontend.Core.Miscellaneous; 7 7 using Iceshrimp.Shared.Schemas; 8 + using Microsoft.AspNetCore.Components.Forms; 8 9 using Microsoft.AspNetCore.Http; 9 10 10 11 namespace Iceshrimp.Frontend.Core.Services; ··· 76 77 HttpMethod method, string path, QueryString? query, object? data 77 78 ) 78 79 { 79 - var body = data != null ? JsonSerializer.Serialize(data) : null; 80 80 var request = new HttpRequestMessage(method, "/api/iceshrimp/" + path.TrimStart('/') + query); 81 81 request.Headers.Accept.ParseAdd(MediaTypeNames.Application.Json); 82 82 if (_token != null) request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _token); 83 - if (body != null) request.Content = new StringContent(body, Encoding.UTF8, MediaTypeNames.Application.Json); 83 + 84 + if (data is IBrowserFile file) 85 + { 86 + request.Content = new MultipartContent 87 + { 88 + new StreamContent(file.OpenReadStream()) 89 + { 90 + Headers = 91 + { 92 + ContentType = new MediaTypeHeaderValue(file.ContentType), 93 + ContentDisposition = new ContentDispositionHeaderValue("attachment") 94 + { 95 + FileName = file.Name, Size = file.Size 96 + } 97 + } 98 + } 99 + }; 100 + } 101 + else if (data is not null) 102 + { 103 + request.Content = new StringContent(JsonSerializer.Serialize(data), Encoding.UTF8, 104 + MediaTypeNames.Application.Json); 105 + } 84 106 85 107 return await client.SendAsync(request); 86 108 }
+2 -1
Iceshrimp.Frontend/Core/Services/ApiService.cs
··· 7 7 public readonly NoteControllerModel Notes = new(client); 8 8 public readonly UserControllerModel Users = new(client); 9 9 public readonly AuthControllerModel Auth = new(client); 10 + public readonly DriveControllerModel Drive = new(client); 10 11 public readonly AdminControllerModel Admin = new(client); 11 12 public readonly TimelineControllerModel Timelines = new(client); 12 13 public readonly NotificationControllerModel Notifications = new(client); 13 - 14 + 14 15 public void SetBearerToken(string token) => client.SetToken(token); 15 16 }
+14
Iceshrimp.Shared/Schemas/DriveFileResponse.cs
··· 1 + using J = System.Text.Json.Serialization.JsonPropertyNameAttribute; 2 + 3 + namespace Iceshrimp.Shared.Schemas; 4 + 5 + public class DriveFileResponse 6 + { 7 + [J("id")] public required string Id { get; set; } 8 + [J("url")] public required string Url { get; set; } 9 + [J("thumbnailUrl")] public required string ThumbnailUrl { get; set; } 10 + [J("filename")] public required string Filename { get; set; } 11 + [J("contentType")] public required string ContentType { get; set; } 12 + [J("sensitive")] public required bool Sensitive { get; set; } 13 + [J("description")] public required string? Description { get; set; } 14 + }
+1
Iceshrimp.Shared/Schemas/NoteCreateRequest.cs
··· 8 8 [J("cw")] public string? Cw { get; set; } 9 9 [J("replyId")] public string? ReplyId { get; set; } 10 10 [J("renoteId")] public string? RenoteId { get; set; } 11 + [J("mediaIds")] public List<string>? MediaIds { get; set; } 11 12 [J("visibility")] public required NoteVisibility Visibility { get; set; } 12 13 }
+10
Iceshrimp.Shared/Schemas/UpdateDriveFileRequest.cs
··· 1 + using J = System.Text.Json.Serialization.JsonPropertyNameAttribute; 2 + 3 + namespace Iceshrimp.Shared.Schemas; 4 + 5 + public class UpdateDriveFileRequest 6 + { 7 + [J("filename")] public string? Filename { get; set; } 8 + [J("sensitive")] public bool? Sensitive { get; set; } 9 + [J("description")] public string? Description { get; set; } 10 + }