fediglam/src/main/api/communities.zig

103 lines
3.1 KiB
Zig
Raw Normal View History

2022-09-07 23:14:52 +00:00
const std = @import("std");
const builtin = @import("builtin");
const util = @import("util");
const models = @import("../db/models.zig");
2022-09-08 01:55:55 +00:00
const DbError = @import("../db.zig").ExecError;
2022-09-07 23:14:52 +00:00
const getRandom = @import("../api.zig").getRandom;
const Uuid = util.Uuid;
const CreateError = error{
InvalidOrigin,
UnsupportedScheme,
CommunityExists,
2022-09-08 01:55:55 +00:00
} || DbError;
2022-09-07 23:14:52 +00:00
pub const Scheme = enum {
https,
http,
pub fn jsonStringify(s: Scheme, _: std.json.StringifyOptions, writer: anytype) !void {
return std.fmt.format(writer, "\"{s}\"", .{@tagName(s)});
}
};
pub const Community = struct {
id: Uuid,
2022-09-08 06:56:29 +00:00
owner_id: ?Uuid,
2022-09-07 23:14:52 +00:00
host: []const u8,
name: []const u8,
scheme: Scheme,
};
pub fn create(db: anytype, origin: []const u8, name: ?[]const u8) CreateError!Community {
const scheme_len = firstIndexOf(origin, ':') orelse return error.InvalidOrigin;
const scheme_str = origin[0..scheme_len];
const scheme = std.meta.stringToEnum(Scheme, scheme_str) orelse return error.UnsupportedScheme;
// host must be in the format "{scheme}://{host}"
if (origin.len <= scheme_len + ("://").len or
origin[scheme_len] != ':' or
origin[scheme_len + 1] != '/' or
origin[scheme_len + 2] != '/') return error.InvalidOrigin;
const host = origin[scheme_len + 3 ..];
// community cannot use non-default ports (except for testing)
// NOTE: Do not add, say localhost and localhost:80 or bugs may happen.
// Avoid using non-default ports unless a test can't be conducted without it.
if (firstIndexOf(host, ':') != null and builtin.mode != .Debug) return error.InvalidOrigin;
// community cannot be hosted on a path
if (firstIndexOf(host, '/') != null) return error.InvalidOrigin;
// Require TLS on production builds
if (scheme != .https and builtin.mode != .Debug) return error.UnsupportedScheme;
const id = Uuid.randV4(getRandom());
const community = Community{
.id = id,
2022-09-08 06:56:29 +00:00
.owner_id = null,
2022-09-07 23:14:52 +00:00
.host = host,
.name = name orelse host,
.scheme = scheme,
};
2022-09-08 02:01:24 +00:00
if ((try db.execRow(&.{Uuid}, "SELECT id FROM community WHERE host = ?", .{host}, null)) != null) {
2022-09-07 23:14:52 +00:00
return error.CommunityExists;
}
2022-09-08 02:01:24 +00:00
try db.insert("community", community);
2022-09-07 23:14:52 +00:00
return community;
}
2022-09-08 05:10:58 +00:00
fn firstIndexOf(str: []const u8, ch: u8) ?usize {
2022-09-07 23:14:52 +00:00
for (str) |c, i| {
if (c == ch) return i;
}
return null;
}
2022-09-08 05:10:58 +00:00
pub fn getByHost(db: anytype, host: []const u8, alloc: std.mem.Allocator) !Community {
2022-09-08 06:56:29 +00:00
const result = (try db.execRow(&.{ Uuid, ?Uuid, []const u8, []const u8, Scheme }, "SELECT id, owner_id, host, name, scheme FROM community WHERE host = ?", .{host}, alloc)) orelse return error.NotFound;
2022-09-08 05:10:58 +00:00
return Community{
.id = result[0],
2022-09-08 06:56:29 +00:00
.owner_id = result[1],
.host = result[2],
.name = result[3],
.scheme = result[4],
2022-09-08 05:10:58 +00:00
};
}
2022-09-08 06:56:29 +00:00
pub fn transferOwnership(db: anytype, community_id: Uuid, new_owner: Uuid) !void {
_ = try db.execRow(&.{i64}, "UPDATE community SET owner_id = ? WHERE id = ?", .{ new_owner, community_id }, null);
}