feat(auth): consolidate multi-user auth and admin controls
This commit is contained in:
@@ -1,21 +1,46 @@
|
||||
/*
|
||||
Warnings:
|
||||
-- NOTE:
|
||||
-- This migration assigns all pre-existing data to a bootstrap admin user so that
|
||||
-- upgrading an existing (non-empty) database doesn't fail and the data remains accessible.
|
||||
-- The bootstrap admin user starts inactive and must be activated via the app's
|
||||
-- initial registration flow.
|
||||
|
||||
- Added the required column `userId` to the `Collection` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `userId` to the `Drawing` table without a default value. This is not possible if the table is not empty.
|
||||
-- Constants
|
||||
-- Keep in sync with backend/src/auth.ts
|
||||
-- (SQLite doesn't support variables; we inline the values instead.)
|
||||
-- BOOTSTRAP_USER_ID = 'bootstrap-admin'
|
||||
-- BOOTSTRAP_LIBRARY_ID = 'user_bootstrap-admin'
|
||||
|
||||
*/
|
||||
-- CreateTable
|
||||
CREATE TABLE "User" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY,
|
||||
"username" TEXT,
|
||||
"email" TEXT NOT NULL,
|
||||
"passwordHash" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"role" TEXT NOT NULL DEFAULT 'USER',
|
||||
"mustResetPassword" BOOLEAN NOT NULL DEFAULT false,
|
||||
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "SystemConfig" (
|
||||
"id" TEXT NOT NULL PRIMARY KEY DEFAULT 'default',
|
||||
"registrationEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL
|
||||
);
|
||||
|
||||
-- Bootstrap state:
|
||||
-- - Insert a singleton config row (registration disabled by default)
|
||||
-- - Insert an inactive bootstrap admin user and assign all existing data to it
|
||||
INSERT INTO "SystemConfig" ("id", "registrationEnabled", "createdAt", "updatedAt")
|
||||
VALUES ('default', false, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
|
||||
|
||||
INSERT INTO "User" ("id", "username", "email", "passwordHash", "name", "role", "mustResetPassword", "isActive", "createdAt", "updatedAt")
|
||||
VALUES ('bootstrap-admin', NULL, 'bootstrap@excalidash.local', '', 'Bootstrap Admin', 'ADMIN', true, false, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);
|
||||
|
||||
-- RedefineTables
|
||||
PRAGMA defer_foreign_keys=ON;
|
||||
PRAGMA foreign_keys=OFF;
|
||||
@@ -27,7 +52,8 @@ CREATE TABLE "new_Collection" (
|
||||
"updatedAt" DATETIME NOT NULL,
|
||||
CONSTRAINT "Collection_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
||||
);
|
||||
INSERT INTO "new_Collection" ("createdAt", "id", "name", "updatedAt") SELECT "createdAt", "id", "name", "updatedAt" FROM "Collection";
|
||||
INSERT INTO "new_Collection" ("createdAt", "id", "name", "userId", "updatedAt")
|
||||
SELECT "createdAt", "id", "name", 'bootstrap-admin', "updatedAt" FROM "Collection";
|
||||
DROP TABLE "Collection";
|
||||
ALTER TABLE "new_Collection" RENAME TO "Collection";
|
||||
CREATE TABLE "new_Drawing" (
|
||||
@@ -45,7 +71,8 @@ CREATE TABLE "new_Drawing" (
|
||||
CONSTRAINT "Drawing_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
CONSTRAINT "Drawing_collectionId_fkey" FOREIGN KEY ("collectionId") REFERENCES "Collection" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);
|
||||
INSERT INTO "new_Drawing" ("appState", "collectionId", "createdAt", "elements", "files", "id", "name", "preview", "updatedAt", "version") SELECT "appState", "collectionId", "createdAt", "elements", "files", "id", "name", "preview", "updatedAt", "version" FROM "Drawing";
|
||||
INSERT INTO "new_Drawing" ("appState", "collectionId", "createdAt", "elements", "files", "id", "name", "preview", "userId", "updatedAt", "version")
|
||||
SELECT "appState", "collectionId", "createdAt", "elements", "files", "id", "name", "preview", 'bootstrap-admin', "updatedAt", "version" FROM "Drawing";
|
||||
DROP TABLE "Drawing";
|
||||
ALTER TABLE "new_Drawing" RENAME TO "Drawing";
|
||||
CREATE TABLE "new_Library" (
|
||||
@@ -54,7 +81,9 @@ CREATE TABLE "new_Library" (
|
||||
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATETIME NOT NULL
|
||||
);
|
||||
INSERT INTO "new_Library" ("createdAt", "id", "items", "updatedAt") SELECT "createdAt", "id", "items", "updatedAt" FROM "Library";
|
||||
-- Migrate the singleton library to the bootstrap user's library key.
|
||||
INSERT INTO "new_Library" ("createdAt", "id", "items", "updatedAt")
|
||||
SELECT "createdAt", 'user_bootstrap-admin', "items", "updatedAt" FROM "Library" WHERE "id" = 'default';
|
||||
DROP TABLE "Library";
|
||||
ALTER TABLE "new_Library" RENAME TO "Library";
|
||||
PRAGMA foreign_keys=ON;
|
||||
@@ -62,3 +91,6 @@ PRAGMA defer_foreign_keys=OFF;
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "User_username_key" ON "User"("username");
|
||||
|
||||
@@ -14,9 +14,12 @@ datasource db {
|
||||
|
||||
model User {
|
||||
id String @id @default(uuid())
|
||||
username String? @unique
|
||||
email String @unique
|
||||
passwordHash String
|
||||
name String
|
||||
role String @default("USER")
|
||||
mustResetPassword Boolean @default(false)
|
||||
isActive Boolean @default(true)
|
||||
drawings Drawing[]
|
||||
collections Collection[]
|
||||
@@ -27,6 +30,13 @@ model User {
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model SystemConfig {
|
||||
id String @id @default("default")
|
||||
registrationEnabled Boolean @default(false)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
model Collection {
|
||||
id String @id @default(uuid())
|
||||
name String
|
||||
|
||||
Reference in New Issue
Block a user