Compare commits
7 Commits
97f9ccec28
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 6d802ef069 | |||
| e0d6035485 | |||
| d917618e67 | |||
| 8fcd0fbf77 | |||
| 31bd0ad0bd | |||
| 2bdf76ab63 | |||
| f4df90ac1e |
@@ -33,6 +33,13 @@ services:
|
||||
GOOGLE_CLIENT_EMAIL: ${GOOGLE_CLIENT_EMAIL}
|
||||
GOOGLE_SHEET_ID: ${GOOGLE_SHEET_ID}
|
||||
GOOGLE_PRIVATE_KEY: ${GOOGLE_PRIVATE_KEY}
|
||||
# Bot de WhatsApp. Sin estas cuatro el .env de produccion no llega al
|
||||
# contenedor: los envios avisan que falta el token y el webhook contesta
|
||||
# 403 porque no puede validar la firma. Ver docs/whatsapp.md.
|
||||
WHATSAPP_PHONE_NUMBER_ID: ${WHATSAPP_PHONE_NUMBER_ID}
|
||||
WHATSAPP_TOKEN: ${WHATSAPP_TOKEN}
|
||||
WHATSAPP_VERIFY_TOKEN: ${WHATSAPP_VERIFY_TOKEN}
|
||||
WHATSAPP_APP_SECRET: ${WHATSAPP_APP_SECRET}
|
||||
ports:
|
||||
- "3004:3000"
|
||||
|
||||
|
||||
+3
-1
@@ -35,7 +35,9 @@ puede confiar en el payload).
|
||||
Para el token definitivo: Business Settings → System Users → crear usuario → Generate Token
|
||||
con permisos `whatsapp_business_messaging` y `whatsapp_business_management`.
|
||||
4. **App → Settings → Basic → App Secret** → `WHATSAPP_APP_SECRET`.
|
||||
5. Cargar las 4 variables en el `.env` de producción y redeployar.
|
||||
5. Cargar las 4 variables en el `.env` de producción (`/home/proyecto/taller_automotriz/.env`,
|
||||
al lado del `docker-compose.yml`, que es de donde compose las interpola) y levantar de nuevo
|
||||
con `docker compose up -d`. Un `git push` no alcanza: el `.env` no está en el repo.
|
||||
6. **WhatsApp → Configuration → Webhook**:
|
||||
- Callback URL: `https://taller.beldo.duckdns.org/api/whatsapp/webhook`
|
||||
- Verify token: el mismo `WHATSAPP_VERIFY_TOKEN`
|
||||
|
||||
@@ -3,7 +3,7 @@ import { prisma } from "@/lib/prisma";
|
||||
import { verificarSesion } from "@/lib/auth-admin";
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
const sesion = await verificarSesion("admin_session");
|
||||
const sesion = await verificarSesion("admin_session", "admin");
|
||||
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const { searchParams } = new URL(req.url);
|
||||
|
||||
@@ -3,7 +3,7 @@ import { prisma } from "@/lib/prisma";
|
||||
import { verificarSesion } from "@/lib/auth-admin";
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
const sesion = await verificarSesion("admin_session");
|
||||
const sesion = await verificarSesion("admin_session", "admin");
|
||||
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const { searchParams } = new URL(req.url);
|
||||
@@ -20,7 +20,7 @@ export async function GET(req: NextRequest) {
|
||||
}
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const sesion = await verificarSesion("admin_session");
|
||||
const sesion = await verificarSesion("admin_session", "admin");
|
||||
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const { autoId, tipoServicioId, descripcion, observaciones, fechaRealizado } = await req.json();
|
||||
|
||||
@@ -4,7 +4,7 @@ import { verificarSesion } from "@/lib/auth-admin";
|
||||
import bcrypt from "bcryptjs";
|
||||
|
||||
export async function PATCH(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||
const sesion = await verificarSesion("admin_session");
|
||||
const sesion = await verificarSesion("admin_session", "admin");
|
||||
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const { id } = await params;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { verificarSesion } from "@/lib/auth-admin";
|
||||
import bcrypt from "bcryptjs";
|
||||
|
||||
export async function GET() {
|
||||
const sesion = await verificarSesion("admin_session");
|
||||
const sesion = await verificarSesion("admin_session", "admin");
|
||||
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const jefes = await prisma.jefeMecanico.findMany({
|
||||
@@ -15,7 +15,7 @@ export async function GET() {
|
||||
}
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const sesion = await verificarSesion("admin_session");
|
||||
const sesion = await verificarSesion("admin_session", "admin");
|
||||
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const { nombre, email, password, slotsParalelos } = await req.json();
|
||||
|
||||
@@ -3,7 +3,7 @@ import { prisma } from "@/lib/prisma";
|
||||
import { verificarSesion } from "@/lib/auth-admin";
|
||||
|
||||
export async function PATCH(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||
const sesion = await verificarSesion("admin_session");
|
||||
const sesion = await verificarSesion("admin_session", "admin");
|
||||
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const { id } = await params;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { prisma } from "@/lib/prisma";
|
||||
import { verificarSesion } from "@/lib/auth-admin";
|
||||
|
||||
export async function GET() {
|
||||
const sesion = await verificarSesion("admin_session");
|
||||
const sesion = await verificarSesion("admin_session", "admin");
|
||||
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const servicios = await prisma.tipoServicio.findMany({ orderBy: { nombre: "asc" } });
|
||||
@@ -11,7 +11,7 @@ export async function GET() {
|
||||
}
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const sesion = await verificarSesion("admin_session");
|
||||
const sesion = await verificarSesion("admin_session", "admin");
|
||||
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const { nombre, duracionHoras } = await req.json();
|
||||
|
||||
@@ -4,7 +4,7 @@ import { verificarSesion } from "@/lib/auth-admin";
|
||||
import { agregarFilaSheet } from "@/lib/google-sheets";
|
||||
|
||||
export async function PATCH(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||
const sesion = await verificarSesion("admin_session");
|
||||
const sesion = await verificarSesion("admin_session", "admin");
|
||||
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const { id } = await params;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { prisma } from "@/lib/prisma";
|
||||
import { verificarSesion } from "@/lib/auth-admin";
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
const sesion = await verificarSesion("admin_session");
|
||||
const sesion = await verificarSesion("admin_session", "admin");
|
||||
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const { searchParams } = new URL(req.url);
|
||||
|
||||
@@ -3,7 +3,7 @@ import { prisma } from "@/lib/prisma";
|
||||
import { verificarSesion } from "@/lib/auth-admin";
|
||||
|
||||
export async function GET() {
|
||||
const sesion = await verificarSesion("cliente_session");
|
||||
const sesion = await verificarSesion("cliente_session", "cliente");
|
||||
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const autos = await prisma.auto.findMany({
|
||||
@@ -15,7 +15,7 @@ export async function GET() {
|
||||
}
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const sesion = await verificarSesion("cliente_session");
|
||||
const sesion = await verificarSesion("cliente_session", "cliente");
|
||||
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const { patente, marca, modelo, anio, nroChasis } = await req.json();
|
||||
|
||||
@@ -3,7 +3,7 @@ import { prisma } from "@/lib/prisma";
|
||||
import { verificarSesion } from "@/lib/auth-admin";
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
const sesion = await verificarSesion("cliente_session");
|
||||
const sesion = await verificarSesion("cliente_session", "cliente");
|
||||
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const { searchParams } = new URL(req.url);
|
||||
|
||||
@@ -3,7 +3,7 @@ import { prisma } from "@/lib/prisma";
|
||||
import { verificarSesion } from "@/lib/auth-admin";
|
||||
|
||||
export async function GET() {
|
||||
const sesion = await verificarSesion("cliente_session");
|
||||
const sesion = await verificarSesion("cliente_session", "cliente");
|
||||
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const jefes = await prisma.jefeMecanico.findMany({
|
||||
|
||||
@@ -6,7 +6,7 @@ import { whereTrabajosDeJefe } from "@/lib/servicios";
|
||||
// Trabajos que ofrece un jefe mecánico: los suyos + los globales del taller.
|
||||
// El flujo del cliente es jefe → trabajo, así que el jefe es obligatorio.
|
||||
export async function GET(req: NextRequest) {
|
||||
const sesion = await verificarSesion("cliente_session");
|
||||
const sesion = await verificarSesion("cliente_session", "cliente");
|
||||
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const jefeMecanicoId = req.nextUrl.searchParams.get("jefeMecanicoId");
|
||||
|
||||
@@ -5,7 +5,7 @@ import { obtenerSlotsDisponibles } from "@/lib/scheduling";
|
||||
import { trabajoEsDeJefe } from "@/lib/servicios";
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
const sesion = await verificarSesion("cliente_session");
|
||||
const sesion = await verificarSesion("cliente_session", "cliente");
|
||||
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const { searchParams } = new URL(req.url);
|
||||
|
||||
@@ -4,7 +4,7 @@ import { verificarSesion } from "@/lib/auth-admin";
|
||||
import { crearTurno } from "@/lib/turnos";
|
||||
|
||||
export async function GET() {
|
||||
const sesion = await verificarSesion("cliente_session");
|
||||
const sesion = await verificarSesion("cliente_session", "cliente");
|
||||
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const turnos = await prisma.turno.findMany({
|
||||
@@ -17,7 +17,7 @@ export async function GET() {
|
||||
}
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const sesion = await verificarSesion("cliente_session");
|
||||
const sesion = await verificarSesion("cliente_session", "cliente");
|
||||
if (!sesion) return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const { autoId, tipoServicioId, jefeMecanicoId, fechaInicio, kilometros } = await req.json();
|
||||
|
||||
@@ -4,8 +4,8 @@ import { verificarSesion } from "@/lib/auth-admin";
|
||||
|
||||
// Edita un trabajo propio del jefe (nombre, duración, activo). Solo si le pertenece.
|
||||
export async function PATCH(req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||
const sesion = await verificarSesion("jefe_session");
|
||||
if (!sesion || sesion.role !== "jefe")
|
||||
const sesion = await verificarSesion("jefe_session", "jefe");
|
||||
if (!sesion)
|
||||
return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const { id } = await params;
|
||||
@@ -42,8 +42,8 @@ export async function PATCH(req: NextRequest, { params }: { params: Promise<{ id
|
||||
// Elimina un trabajo propio. Si ya tiene turnos asociados, lo desactiva en vez de borrarlo
|
||||
// (para no romper la integridad referencial de los turnos históricos).
|
||||
export async function DELETE(_req: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||
const sesion = await verificarSesion("jefe_session");
|
||||
if (!sesion || sesion.role !== "jefe")
|
||||
const sesion = await verificarSesion("jefe_session", "jefe");
|
||||
if (!sesion)
|
||||
return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const { id } = await params;
|
||||
|
||||
@@ -4,8 +4,8 @@ import { verificarSesion } from "@/lib/auth-admin";
|
||||
|
||||
// Lista los trabajos cargados por el jefe logueado (los suyos, no los globales).
|
||||
export async function GET() {
|
||||
const sesion = await verificarSesion("jefe_session");
|
||||
if (!sesion || sesion.role !== "jefe")
|
||||
const sesion = await verificarSesion("jefe_session", "jefe");
|
||||
if (!sesion)
|
||||
return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const servicios = await prisma.tipoServicio.findMany({
|
||||
@@ -18,8 +18,8 @@ export async function GET() {
|
||||
|
||||
// Crea un trabajo propio del jefe (nombre + duración en horas).
|
||||
export async function POST(req: NextRequest) {
|
||||
const sesion = await verificarSesion("jefe_session");
|
||||
if (!sesion || sesion.role !== "jefe")
|
||||
const sesion = await verificarSesion("jefe_session", "jefe");
|
||||
if (!sesion)
|
||||
return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const { nombre, duracionHoras } = await req.json();
|
||||
|
||||
@@ -3,8 +3,8 @@ import { prisma } from "@/lib/prisma";
|
||||
import { verificarSesion } from "@/lib/auth-admin";
|
||||
|
||||
export async function GET(req: NextRequest) {
|
||||
const sesion = await verificarSesion("jefe_session");
|
||||
if (!sesion || sesion.role !== "jefe")
|
||||
const sesion = await verificarSesion("jefe_session", "jefe");
|
||||
if (!sesion)
|
||||
return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
|
||||
const { searchParams } = new URL(req.url);
|
||||
|
||||
+2
-2
@@ -14,8 +14,8 @@ const geistMono = localFont({
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Taller MG",
|
||||
description: "Sistema de turnos - Taller MG",
|
||||
title: "Taller Beldor",
|
||||
description: "Sistema de turnos - Taller Beldor",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
|
||||
@@ -183,7 +183,7 @@ async function guardar(
|
||||
return resp;
|
||||
}
|
||||
|
||||
const BIENVENIDA = "¡Hola! 👋 Soy el asistente del *Taller MG*. Te ayudo a sacar un turno.";
|
||||
const BIENVENIDA = "¡Hola! 👋 Soy el asistente del *Taller Beldor*. Te ayudo a sacar un turno.";
|
||||
|
||||
// ── Entrada principal ──
|
||||
export async function procesarMensaje(telefonoRaw: string, textoRaw: string): Promise<RespuestaBot[]> {
|
||||
|
||||
+8
-6
@@ -3,12 +3,14 @@ import { jwtVerify } from "jose";
|
||||
|
||||
const SECRET = new TextEncoder().encode(process.env.NEXTAUTH_SECRET || "dev-secret");
|
||||
|
||||
async function verifyCookie(req: NextRequest, cookieName: string) {
|
||||
// Las tres sesiones se firman con el mismo secreto, así que además de la firma hay
|
||||
// que mirar el rol: si no, un token de jefe en la cookie del cliente pasa.
|
||||
async function verifyCookie(req: NextRequest, cookieName: string, rol: "admin" | "cliente" | "jefe") {
|
||||
const token = req.cookies.get(cookieName)?.value;
|
||||
if (!token) return false;
|
||||
try {
|
||||
await jwtVerify(token, SECRET);
|
||||
return true;
|
||||
const { payload } = await jwtVerify(token, SECRET);
|
||||
return (payload as { role?: string }).role === rol;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
@@ -21,7 +23,7 @@ export async function middleware(req: NextRequest) {
|
||||
pathname.startsWith("/cliente/historial") ||
|
||||
pathname.startsWith("/cliente/perfil") ||
|
||||
pathname.startsWith("/cliente/turnos")) {
|
||||
const ok = await verifyCookie(req, "cliente_session");
|
||||
const ok = await verifyCookie(req, "cliente_session", "cliente");
|
||||
if (!ok) return NextResponse.redirect(new URL("/cliente", req.url));
|
||||
}
|
||||
|
||||
@@ -31,12 +33,12 @@ export async function middleware(req: NextRequest) {
|
||||
pathname.startsWith("/admin/servicios") ||
|
||||
pathname.startsWith("/admin/jefes") ||
|
||||
pathname.startsWith("/admin/clientes")) {
|
||||
const ok = await verifyCookie(req, "admin_session");
|
||||
const ok = await verifyCookie(req, "admin_session", "admin");
|
||||
if (!ok) return NextResponse.redirect(new URL("/admin", req.url));
|
||||
}
|
||||
|
||||
if (pathname.startsWith("/jefe/dashboard")) {
|
||||
const ok = await verifyCookie(req, "jefe_session");
|
||||
const ok = await verifyCookie(req, "jefe_session", "jefe");
|
||||
if (!ok) return NextResponse.redirect(new URL("/jefe", req.url));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user