Configured Azure AD and Dockerfile

This commit is contained in:
Ahmed Darrazi 2025-12-05 21:00:49 +01:00
parent 85f20147ed
commit ad262a315c
6 changed files with 103 additions and 53 deletions

View File

@ -1,36 +1,47 @@
# 1. Stage: Abhängigkeiten installieren
FROM node:20-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm ci
# 2. Stage: Builder (Der Code wird kompiliert)
# 2. Stage: Builder (Code kompilieren)
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Hier werden auch die Environment Variables für den Build gebraucht
# (In Dokploy setzt du diese später, aber für den Build ignorieren wir ESLint Fehler oft)
ENV NEXT_TELEMETRY_DISABLED 1
ENV NEXT_TELEMETRY_DISABLED=1
RUN npm run build
# 3. Stage: Runner (Das eigentliche Image für Dokploy - winzig klein)
# 3. Stage: Runner (Production Image)
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# Wir kopieren nur das Nötigste aus dem Builder
COPY --from=builder /app/public ./public
# Set correct permissions for prerender cache
RUN mkdir .next
RUN chown nextjs:nodejs .next
# Kopiere den Standalone-Build und Static-Files
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
ENV HOSTNAME "0.0.0.0"
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]

View File

@ -1,76 +1,80 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "tailwindcss";
@theme {
--color-background: hsl(0 0% 100%);
--color-foreground: hsl(0 0% 3.9%);
--color-card: hsl(0 0% 100%);
--color-card-foreground: hsl(0 0% 3.9%);
--color-popover: hsl(0 0% 100%);
--color-popover-foreground: hsl(0 0% 3.9%);
--color-primary: hsl(0 0% 9%);
--color-primary-foreground: hsl(0 0% 98%);
--color-secondary: hsl(0 0% 96.1%);
--color-secondary-foreground: hsl(0 0% 9%);
--color-muted: hsl(0 0% 96.1%);
--color-muted-foreground: hsl(0 0% 45.1%);
--color-accent: hsl(0 0% 96.1%);
--color-accent-foreground: hsl(0 0% 9%);
--color-destructive: hsl(0 84.2% 60.2%);
--color-destructive-foreground: hsl(0 0% 98%);
--color-border: hsl(0 0% 89.8%);
--color-input: hsl(0 0% 89.8%);
--color-ring: hsl(0 0% 3.9%);
--radius: 0.5rem;
}
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--card: 0 0% 100%;
--card-foreground: 0 0% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 0 0% 3.9%;
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
--secondary: 0 0% 96.1%;
--secondary-foreground: 0 0% 9%;
--muted: 0 0% 96.1%;
--muted-foreground: 0 0% 45.1%;
--accent: 0 0% 96.1%;
--accent-foreground: 0 0% 9%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 89.8%;
--input: 0 0% 89.8%;
--ring: 0 0% 3.9%;
--radius: 0.5rem;
}
.dark {
--background: 0 0% 3.9%;
--foreground: 0 0% 98%;
--card: 0 0% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 0 0% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 0 0% 9%;
--secondary: 0 0% 14.9%;
--secondary-foreground: 0 0% 98%;
--muted: 0 0% 14.9%;
--muted-foreground: 0 0% 63.9%;
--accent: 0 0% 14.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 14.9%;
--input: 0 0% 14.9%;
--ring: 0 0% 83.1%;
}
}
@layer base {
* {
@apply border-border;
border-color: hsl(var(--border));
}
body {
@apply bg-background text-foreground;
background-color: hsl(var(--background));
color: hsl(var(--foreground));
}
}

View File

@ -24,7 +24,7 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<html lang="en">
<html lang="en" suppressHydrationWarning>
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>

View File

@ -3,14 +3,15 @@ import { DrizzleAdapter } from "@auth/drizzle-adapter";
import { DefaultSession, getServerSession, NextAuthOptions } from "next-auth";
import { Adapter } from "next-auth/adapters";
import { redirect } from "next/navigation";
import { env } from "@/lib/env.mjs"
import { z } from "zod";
import AzureADProvider from "next-auth/providers/azure-ad";
declare module "next-auth" {
interface Session {
user: DefaultSession["user"] & {
id: string;
};
accessToken?: string;
}
}
@ -24,16 +25,43 @@ export type AuthSession = {
} | null;
};
const envSchema = z.object({
AZURE_AD_CLIENT_ID: z.string().min(1),
AZURE_AD_CLIENT_SECRET: z.string().min(1),
});
export const env = envSchema.parse(process.env);
export const authOptions: NextAuthOptions = {
adapter: DrizzleAdapter(db) as Adapter,
callbacks: {
session: ({ session, user }) => {
session.user.id = user.id;
jwt: async ({ token, account }) => {
if (account) {
token.accessToken = account.access_token;
}
return token;
},
session: ({ session, token, user }) => {
if (user) {
session.user.id = user.id;
}
if (token?.accessToken) {
session.accessToken = token.accessToken as string;
}
return session;
},
},
providers: [
AzureADProvider({
clientId: env.AZURE_AD_CLIENT_ID,
clientSecret: env.AZURE_AD_CLIENT_SECRET,
tenantId: "common", // Multi-Tenancy Support
authorization: {
params: {
scope: "openid profile email offline_access User.Read",
},
},
}),
],
};

View File

@ -11,7 +11,7 @@ export const env = createEnv({
NEXTAUTH_SECRET: process.env.NODE_ENV === "production"
? z.string().min(1)
: z.string().min(1).optional(),
: z.string().optional(),
NEXTAUTH_URL: z.preprocess(
// This makes Vercel deployments not fail if you don't set NEXTAUTH_URL
// Since NextAuth.js automatically uses the VERCEL_URL if present.
@ -19,15 +19,21 @@ export const env = createEnv({
// VERCEL_URL doesn't include `https` so it cant be validated as a URL
process.env.VERCEL_URL ? z.string().min(1) : z.string().url()
),
RESEND_API_KEY: z.string().min(1),
STRIPE_SECRET_KEY: z.string().min(1),
STRIPE_WEBHOOK_SECRET: z.string().min(1),
// Azure AD (Microsoft Entra ID) - optional in development
AZURE_AD_CLIENT_ID: z.string().optional(),
AZURE_AD_CLIENT_SECRET: z.string().optional(),
// Optional in development
RESEND_API_KEY: z.string().optional(),
STRIPE_SECRET_KEY: z.string().optional(),
STRIPE_WEBHOOK_SECRET: z.string().optional(),
},
client: {
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: z.string().min(1),
NEXT_PUBLIC_STRIPE_PRO_PRICE_ID: z.string().min(1),
NEXT_PUBLIC_STRIPE_MAX_PRICE_ID: z.string().min(1),
NEXT_PUBLIC_STRIPE_ULTRA_PRICE_ID: z.string().min(1), // NEXT_PUBLIC_PUBLISHABLE_KEY: z.string().min(1),
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: z.string().optional(),
NEXT_PUBLIC_STRIPE_PRO_PRICE_ID: z.string().optional(),
NEXT_PUBLIC_STRIPE_MAX_PRICE_ID: z.string().optional(),
NEXT_PUBLIC_STRIPE_ULTRA_PRICE_ID: z.string().optional(),
},
// If you're using Next.js < 13.4.4, you'll need to specify the runtimeEnv manually
// runtimeEnv: {

View File

@ -2,6 +2,7 @@ import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
output: "standalone", // Erforderlich für Docker-Deployment
reactCompiler: true,
};