fix: move prisma generate to builder stage, add migration and entrypoint, replace custom login with NextAuth credentials provider

This commit is contained in:
Carlos
2026-07-10 21:56:45 +02:00
parent cef5402cfa
commit 583170efd1
8 changed files with 80 additions and 145 deletions
+5 -1
View File
@@ -12,6 +12,8 @@ RUN npm install --frozen-lockfile
FROM base AS builder FROM base AS builder
WORKDIR /app WORKDIR /app
ENV DATABASE_URL="postgresql://user:***@localhost:5432/1000apps" ENV DATABASE_URL="postgresql://user:***@localhost:5432/1000apps"
# Install openssl for Prisma binary target detection
RUN apk add --no-cache openssl
COPY --from=deps /app/node_modules ./node_modules COPY --from=deps /app/node_modules ./node_modules
COPY . . COPY . .
ENV NEXT_TELEMETRY_DISABLED=1 ENV NEXT_TELEMETRY_DISABLED=1
@@ -40,6 +42,8 @@ COPY --from=builder --chown=nextjs:nodejs /app/pages ./pages
COPY --from=builder --chown=nextjs:nodejs /app/lib ./lib COPY --from=builder --chown=nextjs:nodejs /app/lib ./lib
COPY --from=builder --chown=nextjs:nodejs /app/components ./components COPY --from=builder --chown=nextjs:nodejs /app/components ./components
COPY --from=builder --chown=nextjs:nodejs /app/styles ./styles COPY --from=builder --chown=nextjs:nodejs /app/styles ./styles
COPY entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/entrypoint.sh
EXPOSE 3000 EXPOSE 3000
CMD ["npm", "run", "start"] ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
Executable
+6
View File
@@ -0,0 +1,6 @@
#!/bin/sh
set -e
echo "Running Prisma migrations..."
npx prisma migrate deploy
echo "Starting Next.js..."
npm run start
+1 -2
View File
@@ -15,8 +15,7 @@
"next-auth": "^4.24.0", "next-auth": "^4.24.0",
"@next-auth/prisma-adapter": "^1.0.7", "@next-auth/prisma-adapter": "^1.0.7",
"@prisma/client": "^5.0.0", "@prisma/client": "^5.0.0",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3"
"jsonwebtoken": "^9.0.0"
}, },
"devDependencies": { "devDependencies": {
"prisma": "^5.0.0", "prisma": "^5.0.0",
-54
View File
@@ -1,54 +0,0 @@
import { PrismaClient } from "@prisma/client"
import bcrypt from "bcryptjs"
const prisma = new PrismaClient()
export default async function handler(req, res) {
if (req.method !== "POST") {
return res.status(405).json({ error: "Método não permitido" })
}
const { username, password } = req.body
if (!username || !password) {
return res.status(400).json({ error: "Username e senha são obrigatórios" })
}
try {
const user = await prisma.user.findUnique({
where: { username }
})
if (!user) {
return res.status(401).json({ error: "Usuário não encontrado" })
}
if (!user.password) {
return res.status(401).json({ error: "Senha não configurada" })
}
const isValid = await bcrypt.compare(password, user.password)
if (!isValid) {
return res.status(401).json({ error: "Senha incorreta" })
}
// Create a JWT token manually for the session
const token = require("jsonwebtoken").sign(
{ sub: user.id, email: user.email, name: user.name || user.username },
process.env.NEXTAUTH_SECRET || "test-secret-2x4=",
{ expiresIn: "30d" }
)
return res.status(200).json({
ok: true,
user: { id: user.id, email: user.email, name: user.name || user.username },
token
})
} catch (error) {
console.error("Signin error:", error)
return res.status(500).json({ error: "Erro interno do servidor" })
} finally {
await prisma.$disconnect()
}
}
-53
View File
@@ -1,53 +0,0 @@
import { PrismaClient } from "@prisma/client"
import bcrypt from "bcryptjs"
const prisma = new PrismaClient()
export default async function handler(req, res) {
if (req.method !== "POST") {
return res.status(405).json({ error: "Método não permitido" })
}
const { username, password } = req.body
if (!username || !password) {
return res.status(400).json({ error: "Username e senha são obrigatórios" })
}
if (password.length < 6) {
return res.status(400).json({ error: "Senha deve ter pelo menos 6 caracteres" })
}
try {
// Check if username exists
const existingUser = await prisma.user.findUnique({
where: { username }
})
if (existingUser) {
return res.status(400).json({ error: "Nome de usuário já existe" })
}
// Hash password
const hashedPassword = await bcrypt.hash(password, 12)
// Create user
const user = await prisma.user.create({
data: {
username,
password: hashedPassword,
email: `${username}@1000apps.local`
}
})
return res.status(201).json({
message: "Usuário criado com sucesso",
user: { id: user.id, username: user.username }
})
} catch (error) {
console.error("Signup error:", error)
return res.status(500).json({ error: "Erro interno do servidor" })
} finally {
await prisma.$disconnect()
}
}
+28 -35
View File
@@ -1,36 +1,32 @@
import { useState } from "react" import { useState } from 'react'
import { useRouter } from "next/router" import { useRouter } from 'next/router'
import { signIn } from 'next-auth/react'
export default function SignIn() { export default function SignIn() {
const [username, setUsername] = useState("") const [username, setUsername] = useState('')
const [password, setPassword] = useState("") const [password, setPassword] = useState('')
const [error, setError] = useState("") const [error, setError] = useState('')
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const router = useRouter() const router = useRouter()
const handleSubmit = async (e) => { const handleSubmit = async (e) => {
e.preventDefault() e.preventDefault()
setError("") setError('')
setLoading(true) setLoading(true)
try { try {
const res = await fetch("/api/auth/signin", { const result = await signIn('credentials', {
method: "POST", redirect: false,
headers: { "Content-Type": "application/json" }, callbackUrl: '/dashboard',
body: JSON.stringify({ username, password }) username,
password,
}) })
if (result.error) {
const data = await res.json() setError(result.error)
} else if (result.status === 'ok') {
if (res.ok && data.ok) { router.push('/dashboard')
// Set session cookie
document.cookie = `next-auth.session-token=${data.token}; path=/; SameSite=Lax`
router.push("/dashboard")
} else {
setError(data.error || "Erro ao entrar")
} }
} catch (err) { } catch (err) {
setError("Erro de conexão") setError('Authentication error')
} finally { } finally {
setLoading(false) setLoading(false)
} }
@@ -40,45 +36,42 @@ export default function SignIn() {
<div className="min-h-screen bg-gray-50 flex items-center justify-center"> <div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="bg-white p-8 rounded-lg shadow-md w-96"> <div className="bg-white p-8 rounded-lg shadow-md w-96">
<h2 className="text-2xl font-bold mb-6">Entrar - 1000Apps</h2> <h2 className="text-2xl font-bold mb-6">Entrar - 1000Apps</h2>
{error && (
<div className="bg-red-100 text-red-700 p-3 rounded mb-4">
{error}
</div>
)}
<form onSubmit={handleSubmit} className="space-y-4"> <form onSubmit={handleSubmit} className="space-y-4">
<input <input
type="text" type="text"
placeholder="Nome de usuário" placeholder="Nome de usuário"
className="w-full p-2 border rounded"
value={username} value={username}
onChange={(e) => setUsername(e.target.value)} onChange={(e) => setUsername(e.target.value)}
className="w-full p-2 border rounded"
required required
/> />
<input <input
type="password" type="password"
placeholder="Senha" placeholder="Senha"
className="w-full p-2 border rounded"
value={password} value={password}
onChange={(e) => setPassword(e.target.value)} onChange={(e) => setPassword(e.target.value)}
className="w-full p-2 border rounded"
required required
/> />
<button <button
type="submit" type="submit"
disabled={loading} disabled={loading}
className="w-full bg-blue-600 text-white py-2 rounded-lg disabled:opacity-50" className="w-full bg-blue-600 text-white py-2 rounded-lg disabled:opacity-50"
> >
{loading ? "Entrando..." : "Entrar"} {loading ? 'Entrando...' : 'Entrar'}
</button> </button>
</form> </form>
<p className="text-center mt-4 text-sm"> <p className="text-center mt-4 text-sm">
Não tem conta?{" "} Não tem conta?{' '}
<a href="/signup" className="text-blue-600 hover:underline"> <a href="/signup" className="text-blue-600 hover:underline">
Cadastrar Cadastrar
</a> </a>
</p> </p>
{error && (
<div className="mt-4 bg-red-100 text-red-700 p-3 rounded">
{error}
</div>
)}
</div> </div>
</div> </div>
) )
@@ -0,0 +1 @@
{}
@@ -0,0 +1,39 @@
-- CreateTable
CREATE TABLE "User" (
"id" TEXT NOT NULL,
"email" TEXT NOT NULL,
"username" TEXT,
"name" TEXT,
"image" TEXT,
"password" TEXT,
"role" TEXT NOT NULL DEFAULT 'user',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "App" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT,
"version" TEXT NOT NULL DEFAULT '1.0.0',
"entryUrl" TEXT NOT NULL,
"ownerId" TEXT NOT NULL,
"isPublic" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "App_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
-- CreateIndex
CREATE UNIQUE INDEX "User_username_key" ON "User"("username");
-- AddForeignKey
ALTER TABLE "App" ADD CONSTRAINT "App_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;