add attachments
All checks were successful
Server Build / publish (push) Successful in 32s
Voice Build / publish (push) Successful in 24s

This commit is contained in:
olcxja 2026-07-14 19:35:00 +02:00
commit 91f8dd0db5
4 changed files with 149 additions and 3 deletions

View file

@ -863,6 +863,87 @@ public static async Task Auth(HttpContext context, Func<Task> next, IQueryCollec
userLock.Release();
}
}
public static async Task UploadDmAttachmentStaging(HttpContext context, Func<Task> next, IQueryCollection query)
{
if (!query.TryGetValue("id", out var idQuery)) return;
if (!context.Request.Headers.TryGetValue("secret", out var secret)) return;
string id = Utils.GetValidIdOrZero(idQuery.ToString());
string password = await Utils.GetPassword(id);
string secretStr = await Utils.NonceDecryptBody(id, password, secret, false);
string auth = await Utils.Auth(id, secretStr, password);
if (auth != Utils.LOGIN_SUCCESS)
{
await context.Response.WriteAsync(Encryption.Encryption.EncryptString(auth, secretStr));
return;
}
if (IsActionRateLimited(id))
{
await context.Response.WriteAsync(Encryption.Encryption.EncryptString("error:rate.limit", password));
return;
}
byte[] fileBytes = await LarpixServer.Utils.Utils.LoadBodyBytes(context.Request.Body, 50 * 1024 * 1024); // 50MB limit for attachments
if (fileBytes == null || fileBytes.Length == 0)
{
await context.Response.WriteAsync(Encryption.Encryption.EncryptString("error:empty.body", password));
return;
}
string stagingId = Guid.NewGuid().ToString();
string stagingDir = $"{LarpixServer.Utils.Utils.ROOMS_DIR}/dms_staging";
string filePath = $"{stagingDir}/{stagingId}";
await Fs.WriteFile(filePath, fileBytes);
// Fire and forget 10 min timer to delete
_ = Task.Run(async () =>
{
await Task.Delay(TimeSpan.FromMinutes(10));
if (Fs.Exists(filePath))
{
Fs.DeleteFile(filePath);
}
});
await context.Response.WriteAsync(Encryption.Encryption.EncryptString($"success:{stagingId}", password));
}
public static async Task GetDmAttachment(HttpContext context, Func<Task> next, IQueryCollection query)
{
if (!query.TryGetValue("id", out var idQuery)) return;
if (!query.TryGetValue("dm", out var dmQuery)) return;
if (!query.TryGetValue("msg", out var msgQuery)) return;
if (!query.TryGetValue("att", out var attQuery)) return;
if (!context.Request.Headers.TryGetValue("secret", out var secret)) return;
string id = Utils.GetValidIdOrZero(idQuery.ToString());
string password = await Utils.GetPassword(id);
string secretStr = await Utils.NonceDecryptBody(id, password, secret, false);
string auth = await Utils.Auth(id, secretStr, password);
if (auth != Utils.LOGIN_SUCCESS) return; // Don't leak info
string dmId = dmQuery.ToString();
string msgId = msgQuery.ToString();
string attId = attQuery.ToString();
// Check if user is in DM
if (!await Room.Requests.IsMemberOfDm(id, dmId)) return;
string filePath = $"{LarpixServer.Utils.Utils.ROOMS_DIR}/dms/{LarpixServer.Utils.Utils.DOMAIN}/{dmId}/attachments/{msgId}/{attId}";
if (!Fs.Exists(filePath))
{
context.Response.StatusCode = 404;
return;
}
byte[] fileBytes = await Fs.ReadFile(filePath);
context.Response.ContentType = "application/octet-stream";
await context.Response.Body.WriteAsync(fileBytes, 0, fileBytes.Length);
}
}
public class CreateHolder

View file

@ -24,6 +24,30 @@ public class Program
});
var app = builder.Build();
string stagingDir = $"{LarpixServer.Utils.Utils.ROOMS_DIR}/dms_staging";
if (Directory.Exists(stagingDir))
{
foreach (var file in Directory.GetFiles(stagingDir))
{
var fileInfo = new FileInfo(file);
var age = DateTime.UtcNow - fileInfo.LastWriteTimeUtc;
var remaining = TimeSpan.FromMinutes(10) - age;
if (remaining <= TimeSpan.Zero)
{
Fs.DeleteFile(file);
}
else
{
_ = Task.Run(async () =>
{
await Task.Delay(remaining);
if (Fs.Exists(file)) Fs.DeleteFile(file);
});
}
}
}
app.UseWebSockets();
app.Use(async (HttpContext context, Func<Task> next) =>
@ -191,6 +215,12 @@ public class Program
case "/_larpix/upload":
await Account.Requests.Upload(context, next, query);
return;
case "/_larpix/dm/attachment/staging":
await Account.Requests.UploadDmAttachmentStaging(context, next, query);
return;
case "/_larpix/dm/attachment/get":
await Account.Requests.GetDmAttachment(context, next, query);
return;
case "/_larpix/federation":
await Federation.Receiver.FederationRequest(context, next, query, reader);
return;

View file

@ -33,7 +33,7 @@ public class Requests
return true;
}
private static async Task<bool> IsMemberOfDm(string id, string dmId)
public static async Task<bool> IsMemberOfDm(string id, string dmId)
{
if (!IsSafeDmId(dmId)) return false;
string memberPath = $"{ROOMS_DIR}/dms/{DOMAIN}/{dmId}/members/{id};{DOMAIN}";
@ -401,6 +401,41 @@ public class Requests
msg.responded = responded ?? "";
msg.reactions = "";
if (!string.IsNullOrEmpty(msg.attachment) && msg.attachment != "[]")
{
try
{
var attachmentsNode = System.Text.Json.Nodes.JsonNode.Parse(msg.attachment);
if (attachmentsNode is System.Text.Json.Nodes.JsonArray arr)
{
string attDir = $"{ROOMS_DIR}/dms/{DOMAIN}/{dmId}/attachments/{nextId}";
if (!Directory.Exists(attDir)) Directory.CreateDirectory(attDir);
for (int i = 0; i < arr.Count; i++)
{
var attObj = arr[i] as System.Text.Json.Nodes.JsonObject;
if (attObj != null && attObj.ContainsKey("staging_id"))
{
string stagingId = attObj["staging_id"].ToString();
string stagingPath = $"{ROOMS_DIR}/dms_staging/{stagingId}";
if (Fs.Exists(stagingPath))
{
string attId = attObj["id"].ToString();
string destPath = $"{attDir}/{attId}";
System.IO.File.Move(stagingPath, destPath);
}
attObj.Remove("staging_id"); // Clean it up before saving
}
}
msg.attachment = attachmentsNode.ToJsonString();
}
}
catch (Exception ex)
{
Console.WriteLine($"Error processing attachments: {ex}");
}
}
await Fs.WriteFile($"{msgDir}/{nextId}", Encoding.UTF8.GetBytes(JsonSerializer.Serialize(msg, AppJsonSerializerContext.Default.Message)));
await Fs.WriteFile($"{msgDir}/last", Encoding.UTF8.GetBytes(nextId.ToString()));

View file

@ -315,9 +315,9 @@ public class Utils
Directory.CreateDirectory(ROOMS_DIR);
}
if (!Directory.Exists(ROOMS_DIR + "/dms"))
{
Directory.CreateDirectory(ROOMS_DIR + "/dms");
}
if (!Directory.Exists(ROOMS_DIR + "/dms_staging"))
Directory.CreateDirectory(ROOMS_DIR + "/dms_staging");
if (!Directory.Exists(ROOMS_DIR + "/groups"))
{
Directory.CreateDirectory(ROOMS_DIR + "/groups");