🚀 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 }

View File

@@ -0,0 +1,64 @@
// lib/auth/credentials.ts
import bcrypt from "bcryptjs"
import { prisma } from "@/lib/prisma"
export async function hashPassword(password: string): Promise<string> {
return bcrypt.hash(password, 10)
}
export async function verifyPassword(password: string, hashedPassword: string): Promise<boolean> {
return bcrypt.compare(password, hashedPassword)
}
export async function createUser(email: string, password: string, name: string) {
// Verificar se usuário já existe
const existing = await prisma.user.findUnique({ where: { email } })
if (existing) {
throw new Error("Email já cadastrado")
}
// Hash da senha
const hashedPassword = await hashPassword(password)
// Criar usuário
const user = await prisma.user.create({
data: {
email,
password: hashedPassword,
name,
role: "EMPLOYEE"
}
})
return user
}
export async function authenticateUser(email: string, password: string) {
// Buscar usuário
const user = await prisma.user.findUnique({ where: { email } })
if (!user || !user.password) {
return null
}
// Verificar senha
const isValid = await verifyPassword(password, user.password)
if (!isValid) {
return null
}
// Atualizar último login
await prisma.user.update({
where: { id: user.id },
data: { lastLoginAt: new Date() }
})
return {
id: user.id,
email: user.email,
name: user.name,
role: user.role
}
}