using System.ComponentModel.DataAnnotations; namespace DorkNet.Server.Data.Entities; /// /// Metadata for a single saved version of a room's PersistedRoomData /// protobuf. The actual bytes live in S3 at the canonical /// (bucket, key) returned by ; /// this row only carries the lookup string + provenance. /// /// One row per save. Visitors fetching the room get the row whose /// matches the current /// RoomEntity.CurrentDataBlobName; the CDN controller takes /// that name, routes it via , and /// streams bytes out of S3. /// /// Was originally a fat row with a Bytes column; that column /// was dropped once S3 became the canonical store. Only text columns /// remain in the DB. /// public class RoomDataBlobEntity { public long Id { get; set; } /// FK to RoomEntity. Indexed for "give me all versions of /// this room" queries (history, GC). Set to 0 for shared assets /// not bound to a specific room (HTRs, PV images, polaroids). public long RoomId { get; set; } /// The string the client uses as the cdn URL segment. /// converts this into a /// canonical (bucket, key) deterministically. [MaxLength(128)] public string BlobName { get; set; } = string.Empty; /// The player id of the uploader. Audit trail; also used by /// the GC to keep the room owner's saves longer than visitor saves. public long UploadedByPlayerId { get; set; } public DateTime UploadedAt { get; set; } = DateTime.UtcNow; /// Comma-separated list of file names referenced by the /// PersistedRoomData (e.g. invention thumbnails, audio clips). The /// client passes these via the upload's referencedFilenames argument; /// the server uses them later when GC'ing orphaned blobs. [MaxLength(2048)] public string ReferencedFilenamesCsv { get; set; } = string.Empty; /// /// Legacy bytes column. The codebase no longer writes to this — S3 /// is the canonical store — but rows imported / uploaded before the /// S3-only cutover still hold their bytes here, and the backfill /// migrator (POST /api/admin/v1/storage/backfill) reads them /// to upload into S3. After backfill is verified, a follow-up EF /// migration drops this column entirely and only metadata remains. /// Nullable so freshly-inserted rows can omit it without breaking /// SQLite's NOT NULL invariant. /// public byte[]? Bytes { get; set; } }