Merge pull request #9 from xCyanGrizzly/copilot/fix-admin-access-issue

Make all users admins in self-hosted deployment
This commit is contained in:
xCyanGrizzly
2026-03-04 21:58:04 +01:00
committed by GitHub
4 changed files with 26 additions and 32 deletions

View File

@@ -0,0 +1,5 @@
-- Promote all existing users to ADMIN (self-hosted: every user is an admin)
UPDATE "User" SET "role" = 'ADMIN' WHERE "role" = 'USER';
-- Change the default role for new users to ADMIN
ALTER TABLE "User" ALTER COLUMN "role" SET DEFAULT 'ADMIN';

View File

@@ -22,7 +22,7 @@ model User {
emailVerified DateTime? emailVerified DateTime?
image String? image String?
hashedPassword String? hashedPassword String?
role Role @default(USER) role Role @default(ADMIN)
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt

View File

@@ -21,27 +21,22 @@ export async function registerUser(input: unknown): Promise<ActionResult<{ id: s
const hashedPassword = await bcrypt.hash(parsed.data.password, 10); const hashedPassword = await bcrypt.hash(parsed.data.password, 10);
// First user to register becomes ADMIN (self-hosted owner) // Self-hosted: all users are admins
const user = await prisma.$transaction(async (tx) => { const user = await prisma.user.create({
const userCount = await tx.user.count(); data: {
const role = userCount === 0 ? "ADMIN" : "USER"; name: parsed.data.name,
email: parsed.data.email,
return tx.user.create({ hashedPassword,
data: { role: "ADMIN",
name: parsed.data.name, settings: {
email: parsed.data.email, create: {
hashedPassword, lowStockThreshold: 10,
role, currency: "USD",
settings: { theme: "dark",
create: { units: "metric",
lowStockThreshold: 10,
currency: "USD",
theme: "dark",
units: "metric",
},
}, },
}, },
}); },
}); });
return { success: true, data: { id: user.id } }; return { success: true, data: { id: user.id } };

View File

@@ -18,12 +18,12 @@ export const { auth, handlers, signIn, signOut } = NextAuth({
async jwt({ token, user }) { async jwt({ token, user }) {
if (user) { if (user) {
token.id = user.id!; token.id = user.id!;
// Fetch the role from the database to pick up first-user ADMIN promotion // Fetch the role from the database to ensure token reflects current role
const dbUser = await prisma.user.findUnique({ const dbUser = await prisma.user.findUnique({
where: { id: user.id! }, where: { id: user.id! },
select: { role: true }, select: { role: true },
}); });
token.role = dbUser?.role ?? user.role ?? "USER"; token.role = dbUser?.role ?? user.role ?? "ADMIN";
} }
return token; return token;
}, },
@@ -38,17 +38,11 @@ export const { auth, handlers, signIn, signOut } = NextAuth({
events: { events: {
async createUser({ user }) { async createUser({ user }) {
if (user.id) { if (user.id) {
// First user to register becomes ADMIN (self-hosted owner) // Self-hosted: all users are admins
const adminExists = await prisma.user.findFirst({ await prisma.user.update({
where: { role: "ADMIN" }, where: { id: user.id },
select: { id: true }, data: { role: "ADMIN" },
}); });
if (!adminExists) {
await prisma.user.update({
where: { id: user.id },
data: { role: "ADMIN" },
});
}
await prisma.userSettings.upsert({ await prisma.userSettings.upsert({
where: { userId: user.id }, where: { userId: user.id },