Zeeshaun a8db66b93e
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 48s
Deploy Promiscuity Character API / deploy (push) Successful in 59s
Deploy Promiscuity Inventory API / deploy (push) Successful in 46s
Deploy Promiscuity Locations API / deploy (push) Successful in 1m0s
Deploy Promiscuity Mail API / deploy (push) Successful in 1m9s
k8s smoke test / test (push) Successful in 9s
Mail support
2026-03-23 20:18:23 -05:00

138 lines
5.1 KiB
C#

using MailApi.Models;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;
namespace MailApi.Services;
public class MailStore
{
private readonly IMongoCollection<MailMessage> _messages;
private readonly IMongoCollection<CharacterDocument> _characters;
public MailStore(IConfiguration cfg)
{
var cs = cfg["MongoDB:ConnectionString"] ?? "mongodb://127.0.0.1:27017";
var dbName = cfg["MongoDB:DatabaseName"] ?? "promiscuity";
var client = new MongoClient(cs);
var db = client.GetDatabase(dbName);
_messages = db.GetCollection<MailMessage>("MailMessages");
_characters = db.GetCollection<CharacterDocument>("Characters");
_messages.Indexes.CreateOne(new CreateIndexModel<MailMessage>(
Builders<MailMessage>.IndexKeys.Ascending(m => m.RecipientCharacterId).Descending(m => m.CreatedUtc)));
_messages.Indexes.CreateOne(new CreateIndexModel<MailMessage>(
Builders<MailMessage>.IndexKeys.Ascending(m => m.SenderCharacterId).Descending(m => m.CreatedUtc)));
}
public async Task<CharacterAccessResult> ResolveCharacterAsync(string characterId, string userId, bool allowAnyOwner)
{
var character = await _characters.Find(c => c.Id == characterId).FirstOrDefaultAsync();
if (character is null)
return new CharacterAccessResult { Exists = false };
return new CharacterAccessResult
{
Exists = true,
IsAuthorized = allowAnyOwner || character.OwnerUserId == userId,
CharacterId = character.Id ?? string.Empty,
CharacterName = character.Name,
OwnerUserId = character.OwnerUserId
};
}
public async Task<List<MailMessage>> GetInboxAsync(string characterId) =>
await _messages.Find(m => m.RecipientCharacterId == characterId)
.SortByDescending(m => m.CreatedUtc)
.ToListAsync();
public async Task<List<MailMessage>> GetSentAsync(string characterId) =>
await _messages.Find(m => m.SenderCharacterId == characterId)
.SortByDescending(m => m.CreatedUtc)
.ToListAsync();
public async Task<SendMailResult> SendAsync(string senderCharacterId, string recipientCharacterName, string subject, string body)
{
var sender = await _characters.Find(c => c.Id == senderCharacterId).FirstOrDefaultAsync();
if (sender is null)
return new SendMailResult { Status = SendMailStatus.SenderNotFound };
var normalizedRecipientName = recipientCharacterName.Trim();
var recipients = await _characters.Find(c => c.Name == normalizedRecipientName).ToListAsync();
if (recipients.Count == 0)
return new SendMailResult { Status = SendMailStatus.RecipientNotFound };
if (recipients.Count > 1)
return new SendMailResult { Status = SendMailStatus.RecipientAmbiguous };
var recipient = recipients[0];
if (recipient.Id == sender.Id)
return new SendMailResult { Status = SendMailStatus.Invalid };
var message = new MailMessage
{
SenderCharacterId = sender.Id ?? string.Empty,
SenderCharacterName = sender.Name,
RecipientCharacterId = recipient.Id ?? string.Empty,
RecipientCharacterName = recipient.Name,
Subject = subject.Trim(),
Body = body.Trim(),
CreatedUtc = DateTime.UtcNow
};
await _messages.InsertOneAsync(message);
return new SendMailResult { Status = SendMailStatus.Ok, Message = message };
}
public async Task<MailMessage?> MarkReadAsync(string characterId, string messageId)
{
var filter = Builders<MailMessage>.Filter.And(
Builders<MailMessage>.Filter.Eq(m => m.Id, messageId),
Builders<MailMessage>.Filter.Eq(m => m.RecipientCharacterId, characterId)
);
var update = Builders<MailMessage>.Update.Set(m => m.ReadUtc, DateTime.UtcNow);
var options = new FindOneAndUpdateOptions<MailMessage> { ReturnDocument = ReturnDocument.After };
return await _messages.FindOneAndUpdateAsync(filter, update, options);
}
public class CharacterAccessResult
{
public bool Exists { get; set; }
public bool IsAuthorized { get; set; }
public string CharacterId { get; set; } = string.Empty;
public string CharacterName { get; set; } = string.Empty;
public string OwnerUserId { get; set; } = string.Empty;
}
public class SendMailResult
{
public SendMailStatus Status { get; set; }
public MailMessage? Message { get; set; }
}
public enum SendMailStatus
{
Ok,
SenderNotFound,
RecipientNotFound,
RecipientAmbiguous,
Invalid
}
[BsonIgnoreExtraElements]
private class CharacterDocument
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string? Id { get; set; }
public string OwnerUserId { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
}
}