6 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
987167de0c Enable worker service by default in docker-compose
Remove profiles from worker service in both docker-compose.yml and
docker-compose.dev.yml so the worker starts automatically with
`docker compose up`. This fixes the issue where verification SMS and
the scheduler timer were not working because the worker was never
started. The bot remains as an optional profile.

Update README to reflect the change.

Co-authored-by: xCyanGrizzly <53275238+xCyanGrizzly@users.noreply.github.com>
2026-03-04 21:13:00 +00:00
copilot-swe-agent[bot]
4f331d5411 Initial plan 2026-03-04 21:09:51 +00:00
xCyanGrizzly
8088a86feb Merge pull request #9 from xCyanGrizzly/copilot/fix-admin-access-issue
Make all users admins in self-hosted deployment
2026-03-04 21:58:04 +01:00
copilot-swe-agent[bot]
b53934ebf2 Make all users admins: update schema default, add migration, simplify registration and OAuth flows
Co-authored-by: xCyanGrizzly <53275238+xCyanGrizzly@users.noreply.github.com>
2026-03-04 20:23:54 +00:00
copilot-swe-agent[bot]
464c86b32a Initial plan 2026-03-04 20:16:22 +00:00
xCyanGrizzly
fc00fb6f2e Merge pull request #8 from xCyanGrizzly/copilot/fix-admin-account-login
Fix first user not getting ADMIN role when signing up via OAuth
2026-03-04 20:24:38 +01:00
7 changed files with 29 additions and 40 deletions

View File

@@ -125,18 +125,15 @@ docker compose up -d
The app will be available at [http://localhost:3000](http://localhost:3000).
### Adding Telegram Services
### Adding the Telegram Bot
The worker and bot run as optional profiles so `docker compose up` works with just the app + database:
The worker starts by default with `docker compose up`. The bot runs as an optional profile:
```bash
# App + DB + Telegram worker (needs TELEGRAM_API_ID + TELEGRAM_API_HASH in .env)
docker compose --profile telegram up -d
# App + DB + Worker + Bot (also needs BOT_TOKEN in .env)
docker compose --profile full up -d
# Or just the bot (alongside app + db)
# Or just the bot (alongside app + db + worker)
docker compose --profile bot up -d
```

View File

@@ -16,7 +16,6 @@ services:
retries: 5
worker:
profiles: ["telegram", "full"]
build:
context: .
dockerfile: worker/Dockerfile

View File

@@ -31,7 +31,6 @@ services:
- frontend
worker:
profiles: ["telegram", "full"]
build:
context: .
dockerfile: worker/Dockerfile

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?
image String?
hashedPassword String?
role Role @default(USER)
role Role @default(ADMIN)
createdAt DateTime @default(now())
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);
// First user to register becomes ADMIN (self-hosted owner)
const user = await prisma.$transaction(async (tx) => {
const userCount = await tx.user.count();
const role = userCount === 0 ? "ADMIN" : "USER";
return tx.user.create({
data: {
name: parsed.data.name,
email: parsed.data.email,
hashedPassword,
role,
settings: {
create: {
lowStockThreshold: 10,
currency: "USD",
theme: "dark",
units: "metric",
},
// Self-hosted: all users are admins
const user = await prisma.user.create({
data: {
name: parsed.data.name,
email: parsed.data.email,
hashedPassword,
role: "ADMIN",
settings: {
create: {
lowStockThreshold: 10,
currency: "USD",
theme: "dark",
units: "metric",
},
},
});
},
});
return { success: true, data: { id: user.id } };

View File

@@ -18,12 +18,12 @@ export const { auth, handlers, signIn, signOut } = NextAuth({
async jwt({ token, user }) {
if (user) {
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({
where: { id: user.id! },
select: { role: true },
});
token.role = dbUser?.role ?? user.role ?? "USER";
token.role = dbUser?.role ?? user.role ?? "ADMIN";
}
return token;
},
@@ -38,17 +38,11 @@ export const { auth, handlers, signIn, signOut } = NextAuth({
events: {
async createUser({ user }) {
if (user.id) {
// First user to register becomes ADMIN (self-hosted owner)
const adminExists = await prisma.user.findFirst({
where: { role: "ADMIN" },
select: { id: true },
// Self-hosted: all users are admins
await prisma.user.update({
where: { id: user.id },
data: { role: "ADMIN" },
});
if (!adminExists) {
await prisma.user.update({
where: { id: user.id },
data: { role: "ADMIN" },
});
}
await prisma.userSettings.upsert({
where: { userId: user.id },