diff --git a/src/api/services/files.zig b/src/api/services/files.zig new file mode 100644 index 0000000..18c0e9d --- /dev/null +++ b/src/api/services/files.zig @@ -0,0 +1,69 @@ +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, + .owner = owner, + .created_at = now, + }, 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; + } +}; diff --git a/src/main/migrations.zig b/src/main/migrations.zig index 89a63d3..dc97b44 100644 --- a/src/main/migrations.zig +++ b/src/main/migrations.zig @@ -205,4 +205,25 @@ const migrations: []const Migration = &.{ , .down = "DROP TABLE follow", }, + .{ + .name = "files", + .up = + \\CREATE TABLE drive_file( + \\ id UUID NOT NULL PRIMARY KEY, + \\ + \\ filename TEXT NOT NULL, + \\ account_owner_id UUID REFERENCES account(id), + \\ community_owner_id UUID REFERENCES community(id), + \\ size INTEGER NOT NULL, + \\ + \\ created_at TIMESTAMPTZ NOT NULL, + \\ + \\ CHECK( + \\ (account_owner_id IS NULL AND community_owner_id IS NOT NULL) + \\ OR (account_owner_id IS NOT NULL AND community_owner_id IS NULL) + \\ ) + \\); + , + .down = "DROP TABLE drive_file", + }, };