diff --git a/src/api/lib.zig b/src/api/lib.zig index 0b37618..cfc7b45 100644 --- a/src/api/lib.zig +++ b/src/api/lib.zig @@ -265,7 +265,7 @@ pub fn setupAdmin(db: sql.Db, origin: []const u8, username: []const u8, password pub const ApiSource = struct { db_conn_pool: *sql.ConnPool, - pub const Conn = ApiConn(sql.Db); + pub const Conn = ApiConn(sql.Db, services); const root_username = "root"; @@ -310,14 +310,14 @@ pub const ApiSource = struct { } }; -fn ApiConn(comptime DbConn: type) type { +fn ApiConn(comptime DbConn: type, comptime models: anytype) type { return struct { const Self = @This(); db: DbConn, - token_info: ?services.auth.TokenInfo = null, + token_info: ?models.auth.TokenInfo = null, user_id: ?Uuid = null, - community: services.communities.Community, + community: models.communities.Community, allocator: std.mem.Allocator, pub fn close(self: *Self) void { @@ -332,7 +332,7 @@ fn ApiConn(comptime DbConn: type) type { } pub fn login(self: *Self, username: []const u8, password: []const u8) !LoginResponse { - return services.auth.login( + return models.auth.login( self.db, username, self.community.id, @@ -351,7 +351,7 @@ fn ApiConn(comptime DbConn: type) type { }; pub fn verifyAuthorization(self: *Self) !AuthorizationInfo { if (self.token_info) |info| { - const user = try services.actors.get(self.db, info.user_id, self.allocator); + const user = try models.actors.get(self.db, info.user_id, self.allocator); defer util.deepFree(self.allocator, user); const username = try util.deepClone(self.allocator, user.username); @@ -370,21 +370,21 @@ fn ApiConn(comptime DbConn: type) type { return error.TokenRequired; } - pub fn createCommunity(self: *Self, origin: []const u8, name: ?[]const u8) !services.communities.Community { + pub fn createCommunity(self: *Self, origin: []const u8, name: ?[]const u8) !models.communities.Community { if (!self.isAdmin()) { return error.PermissionDenied; } const tx = try self.db.begin(); errdefer tx.rollback(); - const community_id = try services.communities.create( + const community_id = try models.communities.create( tx, origin, .{ .name = name }, self.allocator, ); - const community = services.communities.get( + const community = models.communities.get( tx, community_id, self.allocator, @@ -412,18 +412,18 @@ fn ApiConn(comptime DbConn: type) type { // Users can only make user invites if (options.kind != .user and !self.isAdmin()) return error.PermissionDenied; - const invite_id = try services.invites.create(self.db, user_id, community_id, .{ + const invite_id = try models.invites.create(self.db, user_id, community_id, .{ .name = options.name, .lifespan = options.lifespan, .max_uses = options.max_uses, .kind = options.kind, }, self.allocator); - const invite = try services.invites.get(self.db, invite_id, self.allocator); + const invite = try models.invites.get(self.db, invite_id, self.allocator); errdefer util.deepFree(self.allocator, invite); const url = if (options.to_community) |cid| blk: { - const community = try services.communities.get(self.db, cid, self.allocator); + const community = try models.communities.get(self.db, cid, self.allocator); defer util.deepFree(self.allocator, community); break :blk try std.fmt.allocPrint( @@ -454,7 +454,7 @@ fn ApiConn(comptime DbConn: type) type { }; } - fn isInviteValid(invite: services.invites.Invite) bool { + fn isInviteValid(invite: models.invites.Invite) bool { if (invite.max_uses != null and invite.times_used >= invite.max_uses.?) return false; if (invite.expires_at != null and DateTime.now().isAfter(invite.expires_at.?)) return false; return true; @@ -463,7 +463,7 @@ fn ApiConn(comptime DbConn: type) type { pub fn register(self: *Self, username: []const u8, password: []const u8, opt: RegistrationOptions) !UserResponse { const tx = try self.db.beginOrSavepoint(); const maybe_invite = if (opt.invite_code) |code| - try services.invites.getByCode(tx, code, self.community.id, self.allocator) + try models.invites.getByCode(tx, code, self.community.id, self.allocator) else null; defer if (maybe_invite) |inv| util.deepFree(self.allocator, inv); @@ -477,7 +477,7 @@ fn ApiConn(comptime DbConn: type) type { if (self.community.kind == .admin) @panic("Unimplmented"); - const user_id = try services.auth.register( + const user_id = try models.auth.register( tx, username, password, @@ -493,7 +493,7 @@ fn ApiConn(comptime DbConn: type) type { .user => {}, .system => @panic("System user invites unimplemented"), .community_owner => { - try services.communities.transferOwnership(tx, self.community.id, user_id); + try models.communities.transferOwnership(tx, self.community.id, user_id); }, } @@ -508,7 +508,7 @@ fn ApiConn(comptime DbConn: type) type { } fn getUserUnchecked(self: *Self, db: anytype, user_id: Uuid) !UserResponse { - const user = try services.actors.get(db, user_id, self.allocator); + const user = try models.actors.get(db, user_id, self.allocator); const avatar_url = if (user.avatar_file_id) |fid| try std.fmt.allocPrint( @@ -573,7 +573,7 @@ fn ApiConn(comptime DbConn: type) type { // Only authenticated users can post const user_id = self.user_id orelse return error.TokenRequired; - const note_id = try services.notes.create(self.db, user_id, content, self.allocator); + const note_id = try models.notes.create(self.db, user_id, content, self.allocator); return self.getNote(note_id) catch |err| switch (err) { error.NotFound => error.Unexpected, @@ -582,8 +582,8 @@ fn ApiConn(comptime DbConn: type) type { } pub fn getNote(self: *Self, note_id: Uuid) !NoteResponse { - const note = try services.notes.get(self.db, note_id, self.allocator); - const user = try services.actors.get(self.db, note.author_id, self.allocator); + const note = try models.notes.get(self.db, note_id, self.allocator); + const user = try models.actors.get(self.db, note.author_id, self.allocator); // Only serve community-specific notes on unauthenticated requests if (self.user_id == null) { @@ -602,14 +602,14 @@ fn ApiConn(comptime DbConn: type) type { }; } - pub fn queryCommunities(self: *Self, args: services.communities.QueryArgs) !CommunityQueryResult { + pub fn queryCommunities(self: *Self, args: models.communities.QueryArgs) !CommunityQueryResult { if (!self.isAdmin()) return error.PermissionDenied; - return try services.communities.query(self.db, args, self.allocator); + return try models.communities.query(self.db, args, self.allocator); } pub fn globalTimeline(self: *Self, args: TimelineArgs) !TimelineResult { const all_args = std.mem.zeroInit(NoteQueryArgs, args); - const result = try services.notes.query(self.db, all_args, self.allocator); + const result = try models.notes.query(self.db, all_args, self.allocator); return TimelineResult{ .items = result.items, .prev_page = TimelineArgs.from(result.prev_page), @@ -620,7 +620,7 @@ fn ApiConn(comptime DbConn: type) type { pub fn localTimeline(self: *Self, args: TimelineArgs) !TimelineResult { var all_args = std.mem.zeroInit(NoteQueryArgs, args); all_args.community_id = self.community.id; - const result = try services.notes.query(self.db, all_args, self.allocator); + const result = try models.notes.query(self.db, all_args, self.allocator); return TimelineResult{ .items = result.items, .prev_page = TimelineArgs.from(result.prev_page), @@ -631,9 +631,9 @@ fn ApiConn(comptime DbConn: type) type { pub fn homeTimeline(self: *Self, args: TimelineArgs) !TimelineResult { if (self.user_id == null) return error.NoToken; - var all_args = std.mem.zeroInit(services.notes.QueryArgs, args); + var all_args = std.mem.zeroInit(models.notes.QueryArgs, args); all_args.followed_by = self.user_id; - const result = try services.notes.query(self.db, all_args, self.allocator); + const result = try models.notes.query(self.db, all_args, self.allocator); return TimelineResult{ .items = result.items, .prev_page = TimelineArgs.from(result.prev_page), @@ -642,9 +642,9 @@ fn ApiConn(comptime DbConn: type) type { } pub fn queryFollowers(self: *Self, user_id: Uuid, args: FollowerQueryArgs) !FollowerQueryResult { - var all_args = std.mem.zeroInit(services.follows.QueryArgs, args); + var all_args = std.mem.zeroInit(models.follows.QueryArgs, args); all_args.followee_id = user_id; - const result = try services.follows.query(self.db, all_args, self.allocator); + const result = try models.follows.query(self.db, all_args, self.allocator); return FollowerQueryResult{ .items = result.items, .prev_page = FollowQueryArgs.from(result.prev_page), @@ -653,9 +653,9 @@ fn ApiConn(comptime DbConn: type) type { } pub fn queryFollowing(self: *Self, user_id: Uuid, args: FollowingQueryArgs) !FollowingQueryResult { - var all_args = std.mem.zeroInit(services.follows.QueryArgs, args); + var all_args = std.mem.zeroInit(models.follows.QueryArgs, args); all_args.followed_by_id = user_id; - const result = try services.follows.query(self.db, all_args, self.allocator); + const result = try models.follows.query(self.db, all_args, self.allocator); return FollowingQueryResult{ .items = result.items, .prev_page = FollowQueryArgs.from(result.prev_page), @@ -664,12 +664,12 @@ fn ApiConn(comptime DbConn: type) type { } pub fn follow(self: *Self, followee: Uuid) !void { - const result = try services.follows.create(self.db, self.user_id orelse return error.NoToken, followee, self.allocator); + const result = try models.follows.create(self.db, self.user_id orelse return error.NoToken, followee, self.allocator); defer util.deepFree(self.allocator, result); } pub fn unfollow(self: *Self, followee: Uuid) !void { - const result = try services.follows.delete(self.db, self.user_id orelse return error.NoToken, followee, self.allocator); + const result = try models.follows.delete(self.db, self.user_id orelse return error.NoToken, followee, self.allocator); defer util.deepFree(self.allocator, result); } @@ -690,7 +690,7 @@ fn ApiConn(comptime DbConn: type) type { ); } - fn backendDriveEntryToFrontend(self: *Self, entry: services.drive.Entry, recurse: bool) !DriveEntry { + fn backendDriveEntryToFrontend(self: *Self, entry: models.drive.Entry, recurse: bool) !DriveEntry { return if (entry.file_id) |file_id| .{ .file = .{ .id = entry.id, @@ -699,7 +699,7 @@ fn ApiConn(comptime DbConn: type) type { .path = entry.path, .parent_directory_id = entry.parent_directory_id, - .meta = try services.files.get(self.db, file_id, self.allocator), + .meta = try models.files.get(self.db, file_id, self.allocator), }, } else .{ .dir = .{ @@ -712,7 +712,7 @@ fn ApiConn(comptime DbConn: type) type { .children = blk: { if (!recurse) break :blk null; - const children = try services.drive.list(self.db, entry.id, self.allocator); + const children = try models.drive.list(self.db, entry.id, self.allocator); const result = self.allocator.alloc(DriveEntry, children.len) catch |err| { util.deepFree(self.allocator, children); @@ -741,7 +741,7 @@ fn ApiConn(comptime DbConn: type) type { pub fn driveUpload(self: *Self, meta: UploadFileArgs, body: []const u8) !DriveEntry { const user_id = self.user_id orelse return error.NoToken; - const file_id = try services.files.create(self.db, user_id, .{ + const file_id = try models.files.create(self.db, user_id, .{ .filename = meta.filename, .description = meta.description, .content_type = meta.content_type, @@ -749,11 +749,11 @@ fn ApiConn(comptime DbConn: type) type { }, body, self.allocator); const entry = entry: { - errdefer services.files.delete(self.db, file_id, self.allocator) catch |err| { + errdefer models.files.delete(self.db, file_id, self.allocator) catch |err| { std.log.err("Unable to delete file {}: {}", .{ file_id, err }); }; - break :entry services.drive.create( + break :entry models.drive.create( self.db, user_id, meta.dir, @@ -768,7 +768,7 @@ fn ApiConn(comptime DbConn: type) type { const name = split.rest(); const new_name = try std.fmt.bufPrint(&buf, "{s}.{s}.{s}", .{ name, file_id, ext }); - break :entry try services.drive.create( + break :entry try models.drive.create( self.db, user_id, meta.dir, @@ -787,63 +787,63 @@ fn ApiConn(comptime DbConn: type) type { pub fn driveMkdir(self: *Self, parent_path: []const u8, name: []const u8) !DriveEntry { const user_id = self.user_id orelse return error.NoToken; - const entry = try services.drive.create(self.db, user_id, parent_path, name, null, self.allocator); + const entry = try models.drive.create(self.db, user_id, parent_path, name, null, self.allocator); errdefer util.deepFree(self.allocator, entry); return try self.backendDriveEntryToFrontend(entry, true); } pub fn driveDelete(self: *Self, path: []const u8) !void { const user_id = self.user_id orelse return error.NoToken; - const entry = try services.drive.stat(self.db, user_id, path, self.allocator); + const entry = try models.drive.stat(self.db, user_id, path, self.allocator); defer util.deepFree(self.allocator, entry); - try services.drive.delete(self.db, entry.id, self.allocator); - if (entry.file_id) |file_id| try services.files.delete(self.db, file_id, self.allocator); + try models.drive.delete(self.db, entry.id, self.allocator); + if (entry.file_id) |file_id| try models.files.delete(self.db, file_id, self.allocator); } pub fn driveMove(self: *Self, src: []const u8, dest: []const u8) !DriveEntry { const user_id = self.user_id orelse return error.NoToken; - try services.drive.move(self.db, user_id, src, dest, self.allocator); + try models.drive.move(self.db, user_id, src, dest, self.allocator); return try self.driveGet(dest); } pub fn driveGet(self: *Self, path: []const u8) !DriveEntry { const user_id = self.user_id orelse return error.NoToken; - const entry = try services.drive.stat(self.db, user_id, path, self.allocator); + const entry = try models.drive.stat(self.db, user_id, path, self.allocator); errdefer util.deepFree(self.allocator, entry); return try self.backendDriveEntryToFrontend(entry, true); } - pub fn driveUpdate(self: *Self, path: []const u8, meta: services.files.PartialMeta) !DriveEntry { + pub fn driveUpdate(self: *Self, path: []const u8, meta: models.files.PartialMeta) !DriveEntry { const user_id = self.user_id orelse return error.NoToken; std.log.debug("{s}", .{path}); - const entry = try services.drive.stat(self.db, user_id, path, self.allocator); + const entry = try models.drive.stat(self.db, user_id, path, self.allocator); defer util.deepFree(self.allocator, entry); std.log.debug("{}", .{entry.id}); - try services.files.update(self.db, entry.file_id orelse return error.NotAFile, meta, self.allocator); + try models.files.update(self.db, entry.file_id orelse return error.NotAFile, meta, self.allocator); return try self.driveGet(path); } pub fn fileDereference(self: *Self, id: Uuid) !FileResult { - const meta = try services.files.get(self.db, id, self.allocator); + const meta = try models.files.get(self.db, id, self.allocator); errdefer util.deepFree(self.allocator, meta); return FileResult{ .meta = meta, - .data = try services.files.deref(self.allocator, id), + .data = try models.files.deref(self.allocator, id), }; } pub fn updateUserProfile(self: *Self, id: Uuid, data: PartialUserProfile) !void { if (!Uuid.eql(id, self.user_id orelse return error.NoToken)) return error.AccessDenied; - try services.actors.updateProfile(self.db, id, data, self.allocator); + try models.actors.updateProfile(self.db, id, data, self.allocator); } pub fn validateInvite(self: *Self, code: []const u8) !InviteResponse { - const invite = services.invites.getByCode( + const invite = models.invites.getByCode( self.db, code, self.community.id,