9 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
5eb2cf05b9 Fix Docker deployment: file permissions, missing env vars, healthcheck timing, error handling
Co-authored-by: xCyanGrizzly <53275238+xCyanGrizzly@users.noreply.github.com>
2026-03-04 22:20:49 +00:00
copilot-swe-agent[bot]
f73d06b3d9 Initial plan 2026-03-04 22:06:36 +00:00
xCyanGrizzly
cac3d518e1 Merge pull request #10 from xCyanGrizzly/copilot/debug-docker-compose-worker
Enable worker service by default in docker-compose
2026-03-04 22:49:52 +01:00
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
9 changed files with 44 additions and 48 deletions

View File

@@ -30,19 +30,19 @@ RUN addgroup --system --gid 1001 nodejs && \
adduser --system --uid 1001 nextjs
# Copy public assets
COPY --from=builder /app/public ./public
# Copy prisma schema + migrations for runtime migrate deploy
COPY --from=builder /app/prisma ./prisma
COPY --from=builder /app/prisma.config.ts ./prisma.config.ts
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
# Copy standalone build output
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
# Copy prisma schema + migrations for runtime migrate deploy
COPY --from=builder --chown=nextjs:nodejs /app/prisma ./prisma
COPY --from=builder --chown=nextjs:nodejs /app/prisma.config.ts ./prisma.config.ts
# Copy node_modules for prisma CLI (needed for migrate deploy at startup).
# Copying the full directory ensures all transitive dependencies are present.
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules
# Recreate the .bin/prisma symlink so Node resolves __dirname to prisma/build/,
# where the WASM files live (COPY dereferences symlinks, breaking WASM resolution)
RUN mkdir -p ./node_modules/.bin && \

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

@@ -10,9 +10,13 @@ services:
- DATABASE_URL=postgresql://${POSTGRES_USER:-dragons}:${POSTGRES_PASSWORD:-stash}@db:5432/${POSTGRES_DB:-dragonsstash}
- AUTH_SECRET=${AUTH_SECRET:?Set AUTH_SECRET in .env}
- AUTH_TRUST_HOST=true
- AUTH_GITHUB_ID=${AUTH_GITHUB_ID:-}
- AUTH_GITHUB_SECRET=${AUTH_GITHUB_SECRET:-}
- NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL:-http://localhost:3000}
- TELEGRAM_API_KEY=${TELEGRAM_API_KEY:-}
- BOT_TOKEN=${BOT_TOKEN:-}
- BOT_USERNAME=${BOT_USERNAME:-}
- LOG_LEVEL=${LOG_LEVEL:-info}
depends_on:
db:
condition: service_healthy
@@ -21,7 +25,7 @@ services:
interval: 30s
timeout: 5s
retries: 3
start_period: 30s
start_period: 60s
restart: unless-stopped
deploy:
resources:
@@ -31,7 +35,6 @@ services:
- frontend
worker:
profiles: ["telegram", "full"]
build:
context: .
dockerfile: worker/Dockerfile

View File

@@ -10,7 +10,10 @@ if [ "$AUTH_SECRET" = "change-me-to-a-random-secret-in-production" ] || [ -z "$A
fi
echo "Running database migrations..."
./node_modules/.bin/prisma migrate deploy
if ! ./node_modules/.bin/prisma migrate deploy; then
echo "ERROR: Database migration failed. Check DATABASE_URL and database connectivity."
exit 1
fi
if [ "$SEED_DATABASE" = "true" ]; then
echo "Seeding database..."

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,17 +21,13 @@ 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({
// Self-hosted: all users are admins
const user = await prisma.user.create({
data: {
name: parsed.data.name,
email: parsed.data.email,
hashedPassword,
role,
role: "ADMIN",
settings: {
create: {
lowStockThreshold: 10,
@@ -42,7 +38,6 @@ export async function registerUser(input: unknown): Promise<ActionResult<{ id: s
},
},
});
});
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 },
});
if (!adminExists) {
// Self-hosted: all users are admins
await prisma.user.update({
where: { id: user.id },
data: { role: "ADMIN" },
});
}
await prisma.userSettings.upsert({
where: { userId: user.id },