const std = @import("std"); const util = @import("util"); const Uuid = util.Uuid; const DateTime = util.DateTime; pub const FileOwner = union(enum) { user_id: Uuid, community_id: Uuid, }; pub const DriveFile = struct { id: Uuid, filename: []const u8, owner: FileOwner, size: usize, created_at: DateTime, }; pub const files = struct { pub fn create(db: anytype, owner: FileOwner, filename: []const u8, data: []const u8, alloc: std.mem.Allocator) !void { const id = Uuid.randV4(util.getThreadPrng()); const now = DateTime.now(); // TODO: assert we're not in a transaction db.insert("drive_file", .{ .id = id, .filename = filename, .account_owner_id = if (owner == .user_id) owner.user_id else null, .community_owner_id = if (owner == .community_id) owner.community_id else null, .created_at = now, .size = data.len, }, alloc) catch return error.DatabaseFailure; // Assume the previous statement succeeded and is not stuck in a transaction errdefer { db.exec("DELETE FROM drive_file WHERE ID = $1", .{id}, alloc) catch |err| { std.log.err("Unable to remove file record in DB: {}", .{err}); }; } try saveFile(id, data); } const data_root = "./files"; fn saveFile(id: Uuid, data: []const u8) !void { var dir = try std.fs.cwd().openDir(data_root, .{}); defer dir.close(); var file = try dir.createFile(&id.toCharArray(), .{ .exclusive = true }); defer file.close(); try file.writer().writeAll(data); try file.sync(); } pub fn deref(alloc: std.mem.Allocator, id: Uuid) ![]const u8 { var dir = try std.fs.cwd().openDir(data_root, .{}); defer dir.close(); return dir.readFileAlloc(alloc, &id.toCharArray(), 1 << 32); } pub fn delete(db: anytype, alloc: std.mem.Allocator, id: Uuid) !void { var dir = try std.fs.cwd().openDir(data_root, .{}); defer dir.close(); try dir.deleteFile(id.toCharArray()); db.exec("DELETE FROM drive_file WHERE ID = $1", .{id}, alloc) catch return error.DatabaseFailure; } };