🚀 Initial commit - PDIMaker v1.0.0

Sistema completo de gestão de PDI com:
- Autenticação com email/senha e Google OAuth
- Workspaces privados isolados
- Sistema de convites com código único
- Interface profissional com Next.js 14
- Backend NestJS com PostgreSQL
- Docker com Nginx e SSL

Desenvolvido por Sergio Correa
This commit is contained in:
2025-11-19 02:09:04 +00:00
commit 0524656198
58 changed files with 6660 additions and 0 deletions

136
frontend/lib/auth/config.ts Normal file
View File

@@ -0,0 +1,136 @@
// lib/auth/config.ts
import { NextAuthOptions } from "next-auth"
import CredentialsProvider from "next-auth/providers/credentials"
import GoogleProvider from "next-auth/providers/google"
import { PrismaAdapter } from "@auth/prisma-adapter"
import { prisma } from "@/lib/prisma"
import { authenticateUser } from "./credentials"
export const authOptions: NextAuthOptions = {
adapter: PrismaAdapter(prisma) as any,
providers: [
CredentialsProvider({
name: "Credentials",
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Senha", type: "password" }
},
async authorize(credentials) {
if (!credentials?.email || !credentials?.password) {
return null
}
try {
const user = await authenticateUser(credentials.email, credentials.password)
return user
} catch (error) {
console.error("Erro ao autenticar:", error)
return null
}
}
}),
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID || "",
clientSecret: process.env.GOOGLE_CLIENT_SECRET || "",
}),
],
session: {
strategy: "jwt",
},
pages: {
signIn: "/login",
error: "/login",
newUser: "/onboarding",
},
callbacks: {
async signIn({ user, account, profile }) {
// Verificar se o usuário já existe
const existingUser = await prisma.user.findUnique({
where: { email: user.email! }
})
// Se não existir, criar com dados do Google
if (!existingUser) {
await prisma.user.upsert({
where: { email: user.email! },
create: {
email: user.email!,
name: user.name || user.email!.split("@")[0],
avatar: user.image,
googleId: account?.providerAccountId,
role: "EMPLOYEE"
},
update: {
googleId: account?.providerAccountId,
avatar: user.image,
lastLoginAt: new Date()
}
})
} else {
// Atualizar último login
await prisma.user.update({
where: { id: existingUser.id },
data: { lastLoginAt: new Date() }
})
}
return true
},
async jwt({ token, user }) {
if (user) {
const dbUser = await prisma.user.findUnique({
where: { email: user.email! }
})
if (dbUser) {
token.id = dbUser.id
token.role = dbUser.role
}
}
return token
},
async session({ session, token }) {
if (session.user && token.id) {
const userId = String(token.id);
(session.user as any).id = userId;
(session.user as any).role = token.role;
// Buscar workspaces do usuário
try {
const user = await prisma.user.findUnique({
where: { id: userId },
include: {
workspacesAsEmployee: {
where: { status: "ACTIVE" },
select: { id: true, slug: true, managerId: true },
},
workspacesAsManager: {
where: { status: "ACTIVE" },
select: { id: true, slug: true, employeeId: true },
},
},
})
if (user) {
(session.user as any).workspaces = {
asEmployee: user.workspacesAsEmployee,
asManager: user.workspacesAsManager,
}
}
} catch (error) {
console.error("Erro ao buscar workspaces:", error)
}
}
return session
},
},
}
export { authOptions as auth }