Track invite uses and handle properly
This commit is contained in:
parent
76f2b818ab
commit
ae9a062caf
4 changed files with 13 additions and 12 deletions
|
@ -86,6 +86,12 @@ pub const LoginResult = struct {
|
|||
issued_at: DateTime,
|
||||
};
|
||||
|
||||
pub const InviteOptions = struct {
|
||||
name: []const u8 = "",
|
||||
max_uses: ?i64 = null,
|
||||
lifetime: ?i64 = null, // unix seconds, TODO make a TimeSpan type
|
||||
};
|
||||
|
||||
threadlocal var prng: std.rand.DefaultPrng = undefined;
|
||||
|
||||
pub fn initThreadPrng(seed: u64) void {
|
||||
|
@ -225,7 +231,8 @@ fn ApiConn(comptime DbConn: type) type {
|
|||
const now = DateTime.now();
|
||||
const invite_id = if (info.invite_code) |invite_code| blk: {
|
||||
const invite = (try self.db.getBy(models.Invite, .invite_code, invite_code, self.arena.allocator())) orelse return error.InvalidInvite;
|
||||
const uses_left = if (invite.max_uses) |max_uses| invite.uses < max_uses else true;
|
||||
const uses = try self.db.countWhereEq(models.LocalUser, .invite_id, invite.id);
|
||||
const uses_left = if (invite.max_uses) |max_uses| uses < max_uses else true;
|
||||
const expired = if (invite.expires_at) |expires_at| now.seconds_since_epoch > expires_at.seconds_since_epoch else false;
|
||||
|
||||
if (!uses_left or expired) return error.InvalidInvite;
|
||||
|
@ -306,11 +313,6 @@ fn ApiConn(comptime DbConn: type) type {
|
|||
};
|
||||
}
|
||||
|
||||
const InviteOptions = struct {
|
||||
name: []const u8 = "",
|
||||
max_uses: ?i64 = null,
|
||||
lifetime: ?i64 = null, // unix seconds, TODO make a TimeSpan type
|
||||
};
|
||||
pub fn createInvite(self: *Self, options: InviteOptions) !models.Invite {
|
||||
const id = Uuid.randV4(prng.random());
|
||||
|
||||
|
@ -334,7 +336,6 @@ fn ApiConn(comptime DbConn: type) type {
|
|||
.created_by = user_id,
|
||||
.invite_code = code_str,
|
||||
|
||||
.uses = 0,
|
||||
.max_uses = options.max_uses,
|
||||
|
||||
.created_at = now,
|
||||
|
|
|
@ -3,16 +3,19 @@ const http = @import("http");
|
|||
const Uuid = @import("util").Uuid;
|
||||
|
||||
const utils = @import("../../controllers.zig").utils;
|
||||
const NoteCreateInfo = @import("../../api.zig").NoteCreateInfo;
|
||||
const InviteOptions = @import("../../api.zig").InviteOptions;
|
||||
|
||||
const RequestServer = root.RequestServer;
|
||||
const RouteArgs = http.RouteArgs;
|
||||
|
||||
pub fn create(srv: *RequestServer, ctx: *http.server.Context, _: RouteArgs) !void {
|
||||
const opt = try utils.parseRequestBody(InviteOptions, ctx);
|
||||
defer utils.freeRequestBody(opt, ctx.alloc);
|
||||
|
||||
var api = try utils.getApiConn(srv, ctx);
|
||||
defer api.close();
|
||||
|
||||
const invite = try api.createInvite(.{});
|
||||
const invite = try api.createInvite(opt);
|
||||
|
||||
try utils.respondJson(ctx, .created, invite);
|
||||
}
|
||||
|
|
|
@ -155,7 +155,6 @@ pub const Database = struct {
|
|||
\\
|
||||
\\ username TEXT NOT NULL,
|
||||
\\ email TEXT,
|
||||
\\ invite_id TEXT,
|
||||
\\
|
||||
\\ hashed_password TEXT NOT NULL,
|
||||
\\ password_changed_at INTEGER NOT NULL,
|
||||
|
@ -215,7 +214,6 @@ pub const Database = struct {
|
|||
\\ invite_code TEXT NOT NULL,
|
||||
\\ created_by TEXT NOT NULL REFERENCES local_user(id),
|
||||
\\
|
||||
\\ uses INTEGER NOT NULL,
|
||||
\\ max_uses INTEGER,
|
||||
\\
|
||||
\\ created_at INTEGER NOT NULL,
|
||||
|
|
|
@ -111,7 +111,6 @@ pub const Invite = struct {
|
|||
invite_code: []const u8,
|
||||
created_by: Ref(LocalUser),
|
||||
|
||||
uses: i64,
|
||||
max_uses: ?i64,
|
||||
|
||||
created_at: DateTime,
|
||||
|
|
Loading…
Reference in a new issue