161 lines
4.0 KiB
Zig
161 lines
4.0 KiB
Zig
const std = @import("std");
|
|
const builtin = @import("builtin");
|
|
const util = @import("util");
|
|
const models = @import("../db/models.zig");
|
|
const getRandom = @import("../api.zig").getRandom;
|
|
|
|
const Uuid = util.Uuid;
|
|
const DateTime = util.DateTime;
|
|
|
|
// 9 random bytes = 12 random b64
|
|
const rand_len = 8;
|
|
const code_len = 12;
|
|
|
|
const Encoder = std.base64.url_safe.Encoder;
|
|
const Decoder = std.base64.url_safe.Decoder;
|
|
|
|
pub const InviteType = enum {
|
|
system,
|
|
community_owner,
|
|
user,
|
|
|
|
pub const jsonStringify = defaultJsonStringify(@This());
|
|
};
|
|
|
|
fn defaultJsonStringify(comptime T: type) fn (T, std.json.StringifyOptions, anytype) anyerror!void {
|
|
return struct {
|
|
pub fn jsonStringify(s: T, _: std.json.StringifyOptions, writer: anytype) !void {
|
|
return std.fmt.format(writer, "\"{s}\"", .{@tagName(s)});
|
|
}
|
|
}.jsonStringify;
|
|
}
|
|
|
|
const InviteCount = u16;
|
|
pub const Invite = struct {
|
|
id: Uuid,
|
|
|
|
created_by: Uuid, // User ID
|
|
to_community: ?Uuid,
|
|
name: []const u8,
|
|
code: []const u8,
|
|
|
|
created_at: DateTime,
|
|
times_used: InviteCount,
|
|
|
|
expires_at: ?DateTime,
|
|
max_uses: ?InviteCount,
|
|
|
|
invite_type: InviteType,
|
|
};
|
|
|
|
const DbModel = struct {
|
|
id: Uuid,
|
|
|
|
created_by: Uuid, // User ID
|
|
to_community: ?Uuid,
|
|
name: []const u8,
|
|
code: []const u8,
|
|
|
|
created_at: DateTime,
|
|
expires_at: ?DateTime,
|
|
|
|
max_uses: ?InviteCount,
|
|
|
|
@"type": InviteType,
|
|
};
|
|
fn cloneStr(str: []const u8, alloc: std.mem.Allocator) ![]const u8 {
|
|
const new = try alloc.alloc(u8, str.len);
|
|
std.mem.copy(u8, new, str);
|
|
|
|
return new;
|
|
}
|
|
|
|
pub const InviteOptions = struct {
|
|
name: ?[]const u8 = null,
|
|
max_uses: ?InviteCount = null,
|
|
expires_at: ?DateTime = null,
|
|
invite_type: InviteType = .user,
|
|
};
|
|
|
|
pub fn create(db: anytype, created_by: Uuid, to_community: ?Uuid, options: InviteOptions, alloc: std.mem.Allocator) !Invite {
|
|
var code_bytes: [rand_len]u8 = undefined;
|
|
getRandom().bytes(&code_bytes);
|
|
|
|
const code = try alloc.alloc(u8, code_len);
|
|
errdefer alloc.free(code);
|
|
_ = Encoder.encode(code, &code_bytes);
|
|
|
|
const name = if (options.name) |name|
|
|
try cloneStr(name, alloc)
|
|
else
|
|
try cloneStr(code, alloc);
|
|
errdefer alloc.free(name);
|
|
|
|
const id = Uuid.randV4(getRandom());
|
|
const created_at = DateTime.now();
|
|
|
|
try db.insert("invite", DbModel{
|
|
.id = id,
|
|
|
|
.created_by = created_by,
|
|
.to_community = to_community,
|
|
.name = name,
|
|
.code = code,
|
|
|
|
.created_at = created_at,
|
|
.expires_at = options.expires_at,
|
|
|
|
.max_uses = options.max_uses,
|
|
|
|
.@"type" = options.invite_type,
|
|
});
|
|
|
|
return Invite{
|
|
.id = id,
|
|
|
|
.created_by = created_by,
|
|
.to_community = to_community,
|
|
.name = name,
|
|
.code = code,
|
|
|
|
.created_at = created_at,
|
|
.expires_at = options.expires_at,
|
|
|
|
.times_used = 0,
|
|
.max_uses = options.max_uses,
|
|
|
|
.invite_type = options.invite_type,
|
|
};
|
|
}
|
|
|
|
pub fn getByCode(db: anytype, code: []const u8, alloc: std.mem.Allocator) !Invite {
|
|
const code_clone = try cloneStr(code, alloc);
|
|
const info = (try db.queryRow(&.{ Uuid, Uuid, Uuid, []const u8, DateTime, ?DateTime, InviteCount, ?InviteCount, InviteType },
|
|
\\SELECT
|
|
\\ invite.id, invite.created_by, invite.to_community, invite.name,
|
|
\\ invite.created_at, invite.expires_at,
|
|
\\ COUNT(local_user.user_id) as uses, invite.max_uses,
|
|
\\ invite.type
|
|
\\FROM invite LEFT OUTER JOIN local_user ON invite.id = local_user.invite_id
|
|
\\WHERE invite.code = $1
|
|
\\GROUP BY invite.id
|
|
, .{code}, alloc)) orelse return error.NotFound;
|
|
|
|
return Invite{
|
|
.id = info[0],
|
|
|
|
.created_by = info[1],
|
|
.to_community = info[2],
|
|
.name = info[3],
|
|
.code = code_clone,
|
|
|
|
.created_at = info[4],
|
|
.expires_at = info[5],
|
|
|
|
.times_used = info[6],
|
|
.max_uses = info[7],
|
|
|
|
.invite_type = info[8],
|
|
};
|
|
}
|