fediglam/src/api/lib.zig

288 lines
10 KiB
Zig
Raw Permalink Normal View History

2022-07-13 03:40:48 +00:00
const std = @import("std");
2022-07-13 04:16:33 +00:00
const util = @import("util");
2022-09-15 01:12:07 +00:00
const sql = @import("sql");
2023-01-03 01:21:08 +00:00
const services = @import("./services.zig");
const types = @import("./types.zig");
2022-07-13 03:40:48 +00:00
2022-10-04 02:41:59 +00:00
const DateTime = util.DateTime;
const Uuid = util.Uuid;
2022-07-13 03:40:48 +00:00
2023-01-03 01:21:08 +00:00
pub usingnamespace types;
pub const Actor = types.actors.Actor;
2023-01-04 19:03:23 +00:00
pub const Community = types.communities.Community;
pub const Invite = types.invites.Invite;
pub const Note = types.notes.Note;
pub const Token = types.tokens.Token;
2022-12-21 15:19:13 +00:00
2022-11-19 11:13:05 +00:00
pub const ClusterMeta = struct {
community_count: usize,
user_count: usize,
note_count: usize,
};
2022-10-04 05:41:22 +00:00
pub fn isAdminSetup(db: sql.Db) !bool {
2023-01-04 19:03:23 +00:00
const svc = services.Services(sql.Db){ .db = db };
_ = svc.getAdminCommunityId() catch |err| switch (err) {
2022-09-29 21:52:01 +00:00
error.NotFound => return false,
2023-01-04 19:03:23 +00:00
else => |e| return e,
2022-09-29 21:52:01 +00:00
};
return true;
}
2022-10-04 05:41:22 +00:00
pub fn setupAdmin(db: sql.Db, origin: []const u8, username: []const u8, password: []const u8, allocator: std.mem.Allocator) anyerror!void {
2023-01-03 01:17:42 +00:00
const svc = @import("./services.zig").Services(sql.Db){ .db = db };
const tx = try svc.beginTx();
errdefer tx.rollbackTx();
2022-09-29 21:52:01 +00:00
var arena = std.heap.ArenaAllocator.init(allocator);
defer arena.deinit();
2023-01-03 01:17:42 +00:00
const community_id = try tx.createCommunity(
arena.allocator(),
2022-09-29 21:52:01 +00:00
origin,
.{ .name = "Cluster Admin", .kind = .admin },
);
2023-01-03 01:17:42 +00:00
const user = try @import("./methods/auth.zig").createLocalAccount(
arena.allocator(),
2023-01-02 01:32:17 +00:00
tx,
2023-01-08 23:35:58 +00:00
.{
.username = username,
.password = password,
.community_id = community_id,
.role = .admin,
},
2023-01-02 01:32:17 +00:00
);
2022-09-29 21:52:01 +00:00
2023-01-03 01:17:42 +00:00
try tx.transferCommunityOwnership(community_id, user);
2022-09-29 21:52:01 +00:00
2023-01-03 01:17:42 +00:00
try tx.commitTx();
2022-09-29 21:52:01 +00:00
std.log.info(
"Created admin user {s} (id {}) with cluster admin origin {s} (id {})",
2022-10-04 02:41:59 +00:00
.{ username, user, origin, community_id },
2022-09-29 21:52:01 +00:00
);
}
2022-07-26 02:07:05 +00:00
pub const ApiSource = struct {
2022-10-13 06:19:59 +00:00
db_conn_pool: *sql.ConnPool,
2022-07-15 00:58:08 +00:00
2023-01-04 19:03:23 +00:00
pub const Conn = ApiConn(sql.Db, method_impl);
2022-07-26 02:07:05 +00:00
2022-09-05 08:52:49 +00:00
const root_username = "root";
2022-10-13 06:19:59 +00:00
pub fn init(pool: *sql.ConnPool) !ApiSource {
2022-09-29 21:52:01 +00:00
return ApiSource{
2022-10-13 06:19:59 +00:00
.db_conn_pool = pool,
2022-09-05 08:52:49 +00:00
};
2022-09-07 23:14:52 +00:00
}
pub fn connectUnauthorized(self: *ApiSource, host: []const u8, alloc: std.mem.Allocator) !Conn {
2022-10-13 06:19:59 +00:00
const db = try self.db_conn_pool.acquire();
2022-10-16 12:48:12 +00:00
errdefer db.releaseConnection();
2023-01-04 19:03:23 +00:00
var conn = Conn{
2022-10-04 05:41:22 +00:00
.db = db,
2023-01-01 23:58:17 +00:00
.context = .{
2023-01-04 19:03:23 +00:00
.community = undefined,
2023-01-01 23:58:17 +00:00
},
.allocator = alloc,
2022-07-26 02:07:05 +00:00
};
2023-01-04 19:03:23 +00:00
conn.context.community = try conn.getServices().getCommunityByHost(alloc, host);
return conn;
2022-07-26 02:07:05 +00:00
}
2022-09-05 08:52:49 +00:00
pub fn connectToken(self: *ApiSource, host: []const u8, token: []const u8, alloc: std.mem.Allocator) !Conn {
2023-01-02 01:32:17 +00:00
var conn = try self.connectUnauthorized(host, alloc);
errdefer conn.close();
2023-01-03 01:17:42 +00:00
conn.context.token_info = try @import("./methods/auth.zig").verifyToken(alloc, conn.context, conn.getServices(), token);
2023-01-02 01:32:17 +00:00
return conn;
2022-07-13 03:40:48 +00:00
}
2022-07-26 02:07:05 +00:00
};
2022-07-13 03:40:48 +00:00
2023-01-01 23:58:17 +00:00
pub const ApiContext = struct {
token_info: ?Token.Info = null,
community: Community,
pub fn userId(self: ApiContext) ?Uuid {
2023-01-04 19:03:23 +00:00
if (self.token_info) |t| return t.account_id else return null;
2023-01-01 23:58:17 +00:00
}
2023-01-02 20:38:42 +00:00
pub fn isAdmin(self: ApiContext) bool {
return self.userId() != null and self.community.kind == .admin;
}
2023-01-01 23:58:17 +00:00
};
2023-01-04 19:03:23 +00:00
const method_impl = struct {
const actors = @import("./methods/actors.zig");
2023-01-03 01:21:08 +00:00
const auth = @import("./methods/auth.zig");
2023-01-04 19:03:23 +00:00
const communities = @import("./methods/communities.zig");
const drive = @import("./methods/drive.zig");
const follows = @import("./methods/follows.zig");
const invites = @import("./methods/invites.zig");
const notes = @import("./methods/notes.zig");
2023-01-03 01:21:08 +00:00
const timelines = @import("./methods/timelines.zig");
};
2023-01-04 19:03:23 +00:00
fn ApiConn(comptime DbConn: type, comptime methods: anytype) type {
2022-07-26 02:07:05 +00:00
return struct {
const Self = @This();
2023-01-03 01:21:08 +00:00
const Services = @import("./services.zig").Services(DbConn);
2022-07-18 07:37:10 +00:00
2022-07-26 02:07:05 +00:00
db: DbConn,
2023-01-01 23:58:17 +00:00
context: ApiContext,
allocator: std.mem.Allocator,
2022-07-18 06:11:42 +00:00
2022-07-26 02:07:05 +00:00
pub fn close(self: *Self) void {
2023-01-01 23:58:17 +00:00
util.deepFree(self.allocator, self.context.community);
if (self.context.token_info) |info| util.deepFree(self.allocator, info);
2022-10-13 06:19:59 +00:00
self.db.releaseConnection();
2022-07-26 02:07:05 +00:00
}
2022-07-18 06:11:42 +00:00
2023-01-03 01:21:08 +00:00
fn getServices(self: *Self) Services {
return Services{ .db = self.db };
}
2023-01-04 19:25:08 +00:00
pub fn getCommunity(self: *Self, id: Uuid) !types.communities.Community {
return try methods.communities.get(self.allocator, self.context, self.getServices(), id);
}
2023-01-04 19:03:23 +00:00
pub fn createCommunity(self: *Self, origin: []const u8, name: ?[]const u8) !Uuid {
return try methods.communities.create(
self.allocator,
2023-01-04 19:03:23 +00:00
self.context,
self.getServices(),
origin,
name,
2022-12-12 03:52:11 +00:00
);
2022-07-27 05:02:09 +00:00
}
2022-09-08 06:56:29 +00:00
2023-01-04 19:03:23 +00:00
pub fn createInvite(self: *Self, options: types.invites.CreateOptions) !Uuid {
return methods.invites.create(self.allocator, self.context, self.getServices(), options);
2022-12-10 06:38:04 +00:00
}
2023-01-04 19:25:08 +00:00
pub fn getInvite(self: *Self, id: Uuid) !Invite {
return try methods.invites.get(self.allocator, self.context, self.getServices(), id);
}
2023-01-02 20:38:42 +00:00
pub fn login(self: *Self, username: []const u8, password: []const u8) !Token {
2023-01-03 01:17:42 +00:00
return methods.auth.login(self.allocator, self.context, self.getServices(), username, password);
}
pub fn register(
self: *Self,
username: []const u8,
password: []const u8,
2023-01-04 19:03:23 +00:00
opt: types.auth.RegistrationOptions,
) !Uuid {
2023-01-03 01:17:42 +00:00
return methods.auth.register(self.allocator, self.context, self.getServices(), username, password, opt);
2023-01-02 20:38:42 +00:00
}
2023-01-04 19:03:23 +00:00
pub fn getActor(self: *Self, user_id: Uuid) !Actor {
return methods.actors.get(self.allocator, self.context, self.getServices(), user_id);
2022-09-08 07:52:23 +00:00
}
2023-01-04 19:03:23 +00:00
pub fn createNote(self: *Self, content: []const u8) !Uuid {
return methods.notes.create(self.allocator, self.context, self.getServices(), content);
2022-09-08 07:52:23 +00:00
}
2022-12-21 15:19:13 +00:00
pub fn getNote(self: *Self, note_id: Uuid) !Note {
2023-01-04 19:03:23 +00:00
return methods.notes.get(self.allocator, self.context, self.getServices(), note_id);
2022-09-08 07:52:23 +00:00
}
2022-09-10 04:02:51 +00:00
2023-01-04 19:03:23 +00:00
pub fn queryCommunities(self: *Self, args: types.communities.QueryArgs) !types.communities.QueryResult {
return methods.communities.query(self.allocator, self.context, self.getServices(), args);
2022-09-10 04:02:51 +00:00
}
2022-11-12 12:39:49 +00:00
2023-01-04 19:03:23 +00:00
pub fn globalTimeline(self: *Self, args: types.timelines.TimelineArgs) !methods.timelines.TimelineResult {
2023-01-03 01:21:08 +00:00
return methods.timelines.globalTimeline(self.allocator, self.context, self.getServices(), args);
2022-11-12 12:39:49 +00:00
}
2022-11-12 13:23:55 +00:00
2023-01-04 19:03:23 +00:00
pub fn localTimeline(self: *Self, args: types.timelines.TimelineArgs) !methods.timelines.TimelineResult {
2023-01-03 01:21:08 +00:00
return methods.timelines.localTimeline(self.allocator, self.context, self.getServices(), args);
2022-11-12 13:23:55 +00:00
}
2022-11-14 08:14:29 +00:00
2023-01-04 19:03:23 +00:00
pub fn homeTimeline(self: *Self, args: types.timelines.TimelineArgs) !methods.timelines.TimelineResult {
2023-01-03 01:21:08 +00:00
return methods.timelines.homeTimeline(self.allocator, self.context, self.getServices(), args);
2022-11-14 23:00:01 +00:00
}
2023-01-04 19:03:23 +00:00
pub fn queryFollowers(self: *Self, user_id: Uuid, args: types.follows.FollowerQueryArgs) !types.follows.FollowerQueryResult {
return methods.follows.queryFollowers(self.allocator, self.context, self.getServices(), user_id, args);
2022-11-14 08:14:29 +00:00
}
2023-01-04 19:03:23 +00:00
pub fn queryFollowing(self: *Self, user_id: Uuid, args: types.follows.FollowingQueryArgs) !types.follows.FollowingQueryResult {
return methods.follows.queryFollowing(self.allocator, self.context, self.getServices(), user_id, args);
2022-11-14 08:14:29 +00:00
}
2022-11-14 09:03:11 +00:00
pub fn follow(self: *Self, followee: Uuid) !void {
2023-01-04 19:03:23 +00:00
try methods.follows.follow(self.allocator, self.context, self.getServices(), followee);
2022-11-14 09:03:11 +00:00
}
2022-11-19 11:13:05 +00:00
2022-11-19 11:33:35 +00:00
pub fn unfollow(self: *Self, followee: Uuid) !void {
2023-01-04 19:03:23 +00:00
try methods.follows.unfollow(self.allocator, self.context, self.getServices(), followee);
2022-11-19 11:33:35 +00:00
}
2022-11-19 11:13:05 +00:00
pub fn getClusterMeta(self: *Self) !ClusterMeta {
return try self.db.queryRow(
ClusterMeta,
\\SELECT
\\ COUNT(DISTINCT note.id) AS note_count,
\\ COUNT(DISTINCT actor.id) AS user_count,
\\ COUNT(DISTINCT community.id) AS community_count
\\FROM note, actor, community
\\WHERE
\\ actor.community_id = community.id AND
\\ community.kind != 'admin'
,
.{},
self.allocator,
2022-11-19 11:13:05 +00:00
);
}
2022-12-03 07:44:27 +00:00
2023-01-04 19:03:23 +00:00
pub fn driveUpload(self: *Self, meta: types.drive.UploadArgs, body: []const u8) !Uuid {
return try methods.drive.upload(self.allocator, self.context, self.getServices(), meta, body);
2022-12-03 15:09:29 +00:00
}
2023-01-04 19:03:23 +00:00
pub fn driveMkdir(self: *Self, parent_path: []const u8, name: []const u8) !Uuid {
return try methods.drive.mkdir(self.allocator, self.context, self.getServices(), parent_path, name);
2022-12-06 09:48:36 +00:00
}
pub fn driveDelete(self: *Self, path: []const u8) !void {
2023-01-04 19:03:23 +00:00
return try methods.drive.delete(self.allocator, self.context, self.getServices(), path);
2022-12-06 09:48:36 +00:00
}
2023-01-04 19:03:23 +00:00
pub fn driveMove(self: *Self, src: []const u8, dest: []const u8) !void {
return try methods.drive.move(self.allocator, self.context, self.getServices(), src, dest);
2022-12-06 09:48:36 +00:00
}
2023-01-04 19:03:23 +00:00
pub fn driveGet(self: *Self, path: []const u8) !types.drive.DriveEntry {
return try methods.drive.get(self.allocator, self.context, self.getServices(), path);
2022-12-06 09:48:36 +00:00
}
2023-01-04 19:25:08 +00:00
pub fn driveGetEntryById(self: *Self, id: Uuid) !types.drive.DriveEntry {
return try methods.drive.getById(self.allocator, self.context, self.getServices(), id);
}
2023-01-04 19:03:23 +00:00
pub fn driveUpdate(self: *Self, path: []const u8, meta: types.files.UpdateArgs) !void {
return try methods.drive.update(self.allocator, self.context, self.getServices(), path, meta);
2022-12-03 07:44:27 +00:00
}
2022-12-06 10:53:41 +00:00
2023-01-04 19:03:23 +00:00
pub fn fileDereference(self: *Self, id: Uuid) !types.files.DerefResult {
return try methods.drive.dereference(self.allocator, self.context, self.getServices(), id);
2022-12-06 10:53:41 +00:00
}
2022-12-07 09:59:46 +00:00
2023-01-04 19:03:23 +00:00
pub fn updateUserProfile(self: *Self, id: Uuid, data: types.actors.ProfileUpdateArgs) !void {
try methods.actors.updateProfile(self.allocator, self.context, self.getServices(), id, data);
2022-12-07 09:59:46 +00:00
}
2022-12-10 09:41:56 +00:00
2023-01-04 19:03:23 +00:00
pub fn validateInvite(self: *Self, code: []const u8) !Invite {
return try methods.invites.getByCode(self.allocator, self.context, self.getServices(), code);
2022-12-10 09:41:56 +00:00
}
2022-07-26 02:07:05 +00:00
};
}
2023-01-08 23:35:58 +00:00
test {
std.testing.refAllDecls(@This());
}