Configured Azure AD and Dockerfile
This commit is contained in:
parent
85f20147ed
commit
ad262a315c
@ -1,36 +1,47 @@
|
|||||||
# 1. Stage: Abhängigkeiten installieren
|
# 1. Stage: Abhängigkeiten installieren
|
||||||
FROM node:20-alpine AS deps
|
FROM node:20-alpine AS deps
|
||||||
|
RUN apk add --no-cache libc6-compat
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY package.json package-lock.json* ./
|
COPY package.json package-lock.json* ./
|
||||||
RUN npm ci
|
RUN npm ci
|
||||||
|
|
||||||
# 2. Stage: Builder (Der Code wird kompiliert)
|
# 2. Stage: Builder (Code kompilieren)
|
||||||
FROM node:20-alpine AS builder
|
FROM node:20-alpine AS builder
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY --from=deps /app/node_modules ./node_modules
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
COPY . .
|
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
|
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
|
FROM node:20-alpine AS runner
|
||||||
WORKDIR /app
|
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 addgroup --system --gid 1001 nodejs
|
||||||
RUN adduser --system --uid 1001 nextjs
|
RUN adduser --system --uid 1001 nextjs
|
||||||
|
|
||||||
# Wir kopieren nur das Nötigste aus dem Builder
|
|
||||||
COPY --from=builder /app/public ./public
|
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/standalone ./
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||||
|
|
||||||
USER nextjs
|
USER nextjs
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
ENV PORT 3000
|
|
||||||
ENV HOSTNAME "0.0.0.0"
|
ENV PORT=3000
|
||||||
|
ENV HOSTNAME="0.0.0.0"
|
||||||
|
|
||||||
CMD ["node", "server.js"]
|
CMD ["node", "server.js"]
|
||||||
@ -1,76 +1,80 @@
|
|||||||
@tailwind base;
|
@import "tailwindcss";
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
@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 {
|
@layer base {
|
||||||
:root {
|
:root {
|
||||||
--background: 0 0% 100%;
|
--background: 0 0% 100%;
|
||||||
--foreground: 0 0% 3.9%;
|
--foreground: 0 0% 3.9%;
|
||||||
|
|
||||||
--card: 0 0% 100%;
|
--card: 0 0% 100%;
|
||||||
--card-foreground: 0 0% 3.9%;
|
--card-foreground: 0 0% 3.9%;
|
||||||
|
|
||||||
--popover: 0 0% 100%;
|
--popover: 0 0% 100%;
|
||||||
--popover-foreground: 0 0% 3.9%;
|
--popover-foreground: 0 0% 3.9%;
|
||||||
|
|
||||||
--primary: 0 0% 9%;
|
--primary: 0 0% 9%;
|
||||||
--primary-foreground: 0 0% 98%;
|
--primary-foreground: 0 0% 98%;
|
||||||
|
|
||||||
--secondary: 0 0% 96.1%;
|
--secondary: 0 0% 96.1%;
|
||||||
--secondary-foreground: 0 0% 9%;
|
--secondary-foreground: 0 0% 9%;
|
||||||
|
|
||||||
--muted: 0 0% 96.1%;
|
--muted: 0 0% 96.1%;
|
||||||
--muted-foreground: 0 0% 45.1%;
|
--muted-foreground: 0 0% 45.1%;
|
||||||
|
|
||||||
--accent: 0 0% 96.1%;
|
--accent: 0 0% 96.1%;
|
||||||
--accent-foreground: 0 0% 9%;
|
--accent-foreground: 0 0% 9%;
|
||||||
|
|
||||||
--destructive: 0 84.2% 60.2%;
|
--destructive: 0 84.2% 60.2%;
|
||||||
--destructive-foreground: 0 0% 98%;
|
--destructive-foreground: 0 0% 98%;
|
||||||
|
|
||||||
--border: 0 0% 89.8%;
|
--border: 0 0% 89.8%;
|
||||||
--input: 0 0% 89.8%;
|
--input: 0 0% 89.8%;
|
||||||
--ring: 0 0% 3.9%;
|
--ring: 0 0% 3.9%;
|
||||||
|
|
||||||
--radius: 0.5rem;
|
--radius: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
--background: 0 0% 3.9%;
|
--background: 0 0% 3.9%;
|
||||||
--foreground: 0 0% 98%;
|
--foreground: 0 0% 98%;
|
||||||
|
|
||||||
--card: 0 0% 3.9%;
|
--card: 0 0% 3.9%;
|
||||||
--card-foreground: 0 0% 98%;
|
--card-foreground: 0 0% 98%;
|
||||||
|
|
||||||
--popover: 0 0% 3.9%;
|
--popover: 0 0% 3.9%;
|
||||||
--popover-foreground: 0 0% 98%;
|
--popover-foreground: 0 0% 98%;
|
||||||
|
|
||||||
--primary: 0 0% 98%;
|
--primary: 0 0% 98%;
|
||||||
--primary-foreground: 0 0% 9%;
|
--primary-foreground: 0 0% 9%;
|
||||||
|
|
||||||
--secondary: 0 0% 14.9%;
|
--secondary: 0 0% 14.9%;
|
||||||
--secondary-foreground: 0 0% 98%;
|
--secondary-foreground: 0 0% 98%;
|
||||||
|
|
||||||
--muted: 0 0% 14.9%;
|
--muted: 0 0% 14.9%;
|
||||||
--muted-foreground: 0 0% 63.9%;
|
--muted-foreground: 0 0% 63.9%;
|
||||||
|
|
||||||
--accent: 0 0% 14.9%;
|
--accent: 0 0% 14.9%;
|
||||||
--accent-foreground: 0 0% 98%;
|
--accent-foreground: 0 0% 98%;
|
||||||
|
|
||||||
--destructive: 0 62.8% 30.6%;
|
--destructive: 0 62.8% 30.6%;
|
||||||
--destructive-foreground: 0 0% 98%;
|
--destructive-foreground: 0 0% 98%;
|
||||||
|
|
||||||
--border: 0 0% 14.9%;
|
--border: 0 0% 14.9%;
|
||||||
--input: 0 0% 14.9%;
|
--input: 0 0% 14.9%;
|
||||||
--ring: 0 0% 83.1%;
|
--ring: 0 0% 83.1%;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@layer base {
|
|
||||||
* {
|
* {
|
||||||
@apply border-border;
|
border-color: hsl(var(--border));
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@apply bg-background text-foreground;
|
background-color: hsl(var(--background));
|
||||||
|
color: hsl(var(--foreground));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,7 +24,7 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en" suppressHydrationWarning>
|
||||||
<body
|
<body
|
||||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -3,14 +3,15 @@ import { DrizzleAdapter } from "@auth/drizzle-adapter";
|
|||||||
import { DefaultSession, getServerSession, NextAuthOptions } from "next-auth";
|
import { DefaultSession, getServerSession, NextAuthOptions } from "next-auth";
|
||||||
import { Adapter } from "next-auth/adapters";
|
import { Adapter } from "next-auth/adapters";
|
||||||
import { redirect } from "next/navigation";
|
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" {
|
declare module "next-auth" {
|
||||||
interface Session {
|
interface Session {
|
||||||
user: DefaultSession["user"] & {
|
user: DefaultSession["user"] & {
|
||||||
id: string;
|
id: string;
|
||||||
};
|
};
|
||||||
|
accessToken?: string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,16 +25,43 @@ export type AuthSession = {
|
|||||||
} | null;
|
} | 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 = {
|
export const authOptions: NextAuthOptions = {
|
||||||
adapter: DrizzleAdapter(db) as Adapter,
|
adapter: DrizzleAdapter(db) as Adapter,
|
||||||
callbacks: {
|
callbacks: {
|
||||||
session: ({ session, user }) => {
|
jwt: async ({ token, account }) => {
|
||||||
session.user.id = user.id;
|
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;
|
return session;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
providers: [
|
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",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
22
lib/env.mjs
22
lib/env.mjs
@ -11,7 +11,7 @@ export const env = createEnv({
|
|||||||
|
|
||||||
NEXTAUTH_SECRET: process.env.NODE_ENV === "production"
|
NEXTAUTH_SECRET: process.env.NODE_ENV === "production"
|
||||||
? z.string().min(1)
|
? z.string().min(1)
|
||||||
: z.string().min(1).optional(),
|
: z.string().optional(),
|
||||||
NEXTAUTH_URL: z.preprocess(
|
NEXTAUTH_URL: z.preprocess(
|
||||||
// This makes Vercel deployments not fail if you don't set NEXTAUTH_URL
|
// This makes Vercel deployments not fail if you don't set NEXTAUTH_URL
|
||||||
// Since NextAuth.js automatically uses the VERCEL_URL if present.
|
// 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
|
// 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()
|
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),
|
// Azure AD (Microsoft Entra ID) - optional in development
|
||||||
STRIPE_WEBHOOK_SECRET: z.string().min(1),
|
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: {
|
client: {
|
||||||
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: z.string().min(1),
|
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: z.string().optional(),
|
||||||
NEXT_PUBLIC_STRIPE_PRO_PRICE_ID: z.string().min(1),
|
NEXT_PUBLIC_STRIPE_PRO_PRICE_ID: z.string().optional(),
|
||||||
NEXT_PUBLIC_STRIPE_MAX_PRICE_ID: z.string().min(1),
|
NEXT_PUBLIC_STRIPE_MAX_PRICE_ID: z.string().optional(),
|
||||||
NEXT_PUBLIC_STRIPE_ULTRA_PRICE_ID: z.string().min(1), // NEXT_PUBLIC_PUBLISHABLE_KEY: z.string().min(1),
|
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
|
// If you're using Next.js < 13.4.4, you'll need to specify the runtimeEnv manually
|
||||||
// runtimeEnv: {
|
// runtimeEnv: {
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import type { NextConfig } from "next";
|
|||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
/* config options here */
|
/* config options here */
|
||||||
|
output: "standalone", // Erforderlich für Docker-Deployment
|
||||||
reactCompiler: true,
|
reactCompiler: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user