feat(auth): admin invite links
Replaces the blunt registrationEnabled toggle with per-invite access. Invites are tokenized, single-use, optionally locked to an email, can grant user or admin role, and expire (default 7d, max 90d). - Invite model with tokenHash (bcrypt), email, role, expiresAt, usedAt/usedByUserId. - inviteService: create, list, revoke, findInviteByToken, consumeInvite. Token is shown exactly once at creation. - /admin/invites page: list with status (Active/Used/Expired), generate with email lock + role + custom expiry, copy one-shot URL, revoke. - /register?invite=TOKEN: accepts invite even when registrationEnabled is false; shows a banner; enforces email lock; applies the invite's role on creation; consumes the invite on success. - Linked from the admin navbar.
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "Invite" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"tokenHash" TEXT NOT NULL,
|
||||
"email" TEXT,
|
||||
"role" TEXT NOT NULL DEFAULT 'user',
|
||||
"expiresAt" DATETIME NOT NULL,
|
||||
"usedAt" DATETIME,
|
||||
"usedByUserId" TEXT,
|
||||
"createdById" TEXT,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Invite_tokenHash_key" ON "Invite"("tokenHash");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Invite_tokenHash_idx" ON "Invite"("tokenHash");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Invite_createdById_idx" ON "Invite"("createdById");
|
||||
@@ -41,6 +41,21 @@ model User {
|
||||
@@index([email])
|
||||
}
|
||||
|
||||
model Invite {
|
||||
id String @id @default(cuid())
|
||||
tokenHash String @unique
|
||||
email String? // optional — lock the invite to a specific email
|
||||
role String @default("user") // user | admin
|
||||
expiresAt DateTime
|
||||
usedAt DateTime?
|
||||
usedByUserId String?
|
||||
createdById String?
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([tokenHash])
|
||||
@@index([createdById])
|
||||
}
|
||||
|
||||
model Session {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
|
||||
Reference in New Issue
Block a user