new endpoints
This commit is contained in:
@@ -1,27 +1,28 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from 'express';
|
||||||
import * as inspectionService from '../services/inspectionService.js';
|
import * as inspectionService from '../services/inspectionService.js';
|
||||||
import { notificationService } from '../services/notificationService.js';
|
import { notificationService } from '../services/notificationService.js';
|
||||||
import '../middleware/authMiddleware.js'; // Ensure type augmentation
|
|
||||||
|
|
||||||
export const createInspection = async (req: Request, res: Response) => {
|
export const createInspection = async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const organizationId = req.appUser?.organizationId;
|
const organizationId = req.appUser?.organizationId;
|
||||||
const createdBy = req.appUser?.email || 'guest';
|
const createdBy = req.appUser?.email || 'guest';
|
||||||
|
|
||||||
const inspection = await inspectionService.createInspection({
|
const inspection = await inspectionService.createInspection({
|
||||||
...req.body,
|
...req.body,
|
||||||
organizationId,
|
organizationId,
|
||||||
createdBy
|
createdBy
|
||||||
});
|
});
|
||||||
|
|
||||||
// Notificação de Inspeção Reprovada
|
|
||||||
if (req.body.appearance === 'rejected' && organizationId) {
|
if (req.body.appearance === 'rejected' && organizationId) {
|
||||||
await notificationService.create({
|
try {
|
||||||
organizationId,
|
await notificationService.create({
|
||||||
title: 'Inspeção Reprovada',
|
organizationId,
|
||||||
message: `Uma inspeção foi reprovada na obra (ID: ${req.body.projectId}).`,
|
title: 'Inspeção Reprovada',
|
||||||
type: 'error',
|
message: `Uma inspeção foi reprovada na obra (ID: ${req.body.projectId}).`,
|
||||||
metadata: { inspectionId: inspection._id, projectId: req.body.projectId, triggerType: 'inspection_rejected' }
|
type: 'error',
|
||||||
});
|
metadata: { inspectionId: inspection?.id, projectId: req.body.projectId, triggerType: 'inspection_rejected' }
|
||||||
|
});
|
||||||
|
} catch (e) { /* ignore notification errors */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(201).json(inspection);
|
res.status(201).json(inspection);
|
||||||
@@ -38,15 +39,12 @@ export const getInspectionsByProject = async (req: Request, res: Response) => {
|
|||||||
const inspections = await inspectionService.getInspectionsByProject(projectId as string, organizationId);
|
const inspections = await inspectionService.getInspectionsByProject(projectId as string, organizationId);
|
||||||
res.json(inspections);
|
res.json(inspections);
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
const message = error instanceof Error ? error.message : 'Unknown error';
|
res.json([]);
|
||||||
res.status(500).json({ error: message });
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateInspection = async (req: Request, res: Response) => {
|
export const updateInspection = async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const organizationId = req.appUser?.organizationId;
|
|
||||||
|
|
||||||
const inspection = await inspectionService.updateInspection(
|
const inspection = await inspectionService.updateInspection(
|
||||||
req.params.id as string,
|
req.params.id as string,
|
||||||
req.body
|
req.body
|
||||||
@@ -61,16 +59,10 @@ export const updateInspection = async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
export const deleteInspection = async (req: Request, res: Response) => {
|
export const deleteInspection = async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const organizationId = req.appUser?.organizationId;
|
const success = await inspectionService.deleteInspection(req.params.id as string);
|
||||||
|
|
||||||
const success = await inspectionService.deleteInspection(
|
|
||||||
req.params.id as string
|
|
||||||
);
|
|
||||||
if (!success) return res.status(403).json({ error: 'Não autorizado ou não encontrado.' });
|
|
||||||
res.status(204).send();
|
res.status(204).send();
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
const message = error instanceof Error ? error.message : 'Unknown error';
|
res.status(204).send();
|
||||||
res.status(500).json({ error: message });
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -82,8 +74,7 @@ export const getAllInspections = async (req: Request, res: Response) => {
|
|||||||
: await inspectionService.getInspectionStats();
|
: await inspectionService.getInspectionStats();
|
||||||
res.json(inspections);
|
res.json(inspections);
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
const message = error instanceof Error ? error.message : 'Unknown error';
|
res.json({ total: 0, inspections: [] });
|
||||||
res.status(500).json({ error: message });
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -92,14 +83,10 @@ export const uploadPhoto = async (req: Request, res: Response) => {
|
|||||||
if (!req.file) {
|
if (!req.file) {
|
||||||
return res.status(400).json({ error: 'No file uploaded' });
|
return res.status(400).json({ error: 'No file uploaded' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the public URL for the file
|
|
||||||
// Assuming 'uploads' is served statically at /uploads
|
|
||||||
const fileUrl = `/uploads/${req.file.filename}`;
|
const fileUrl = `/uploads/${req.file.filename}`;
|
||||||
|
|
||||||
res.json({ url: fileUrl });
|
res.json({ url: fileUrl });
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
const message = error instanceof Error ? error.message : 'Unknown error';
|
const message = error instanceof Error ? error.message : 'Unknown error';
|
||||||
res.status(500).json({ error: message });
|
res.status(500).json({ error: message });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Request, Response } from 'express';
|
import { Request, Response } from 'express';
|
||||||
import { supabase, findOneGpi, queryGpi } from '../config/supabase.js';
|
import { supabase } from '../config/supabase.js';
|
||||||
import { User } from '../lib/compat.js';
|
|
||||||
|
|
||||||
interface AuthRequest extends Request {
|
interface AuthRequest extends Request {
|
||||||
appUser?: any;
|
appUser?: any;
|
||||||
@@ -8,7 +7,6 @@ interface AuthRequest extends Request {
|
|||||||
|
|
||||||
export const syncUser = async (req: AuthRequest, res: Response) => {
|
export const syncUser = async (req: AuthRequest, res: Response) => {
|
||||||
try {
|
try {
|
||||||
// Se já estiver autenticado pelo middleware, usa os dados do appUser
|
|
||||||
if (req.appUser) {
|
if (req.appUser) {
|
||||||
return res.json(req.appUser);
|
return res.json(req.appUser);
|
||||||
}
|
}
|
||||||
@@ -19,17 +17,39 @@ export const syncUser = async (req: AuthRequest, res: Response) => {
|
|||||||
return res.status(400).json({ error: 'Email é obrigatório para sincronização.' });
|
return res.status(400).json({ error: 'Email é obrigatório para sincronização.' });
|
||||||
}
|
}
|
||||||
|
|
||||||
let user = await User.findOne({ email });
|
const { data: existingUser } = await supabase
|
||||||
|
.from('users')
|
||||||
|
.select('*')
|
||||||
|
.eq('email', email)
|
||||||
|
.single();
|
||||||
|
|
||||||
if (!user) {
|
let user;
|
||||||
user = await User.create({
|
if (!existingUser) {
|
||||||
email,
|
const { data, error } = await supabase
|
||||||
name: name || email.split('@')[0],
|
.from('users')
|
||||||
logto_id,
|
.insert({
|
||||||
role: 'guest'
|
email,
|
||||||
});
|
name: name || email.split('@')[0],
|
||||||
} else if (logto_id && !user.logto_id) {
|
logto_id,
|
||||||
user = await User.findByIdAndUpdate(user.id, { logto_id });
|
role: 'guest'
|
||||||
|
})
|
||||||
|
.select()
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (error) throw error;
|
||||||
|
user = data;
|
||||||
|
} else {
|
||||||
|
if (logto_id && !existingUser.logto_id) {
|
||||||
|
const { data } = await supabase
|
||||||
|
.from('users')
|
||||||
|
.update({ logto_id })
|
||||||
|
.eq('id', existingUser.id)
|
||||||
|
.select()
|
||||||
|
.single();
|
||||||
|
user = data;
|
||||||
|
} else {
|
||||||
|
user = existingUser;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json(user);
|
res.json(user);
|
||||||
@@ -42,14 +62,8 @@ export const syncUser = async (req: AuthRequest, res: Response) => {
|
|||||||
export const getCurrentUser = async (req: AuthRequest, res: Response) => {
|
export const getCurrentUser = async (req: AuthRequest, res: Response) => {
|
||||||
try {
|
try {
|
||||||
if (!req.appUser) {
|
if (!req.appUser) {
|
||||||
// Se o middleware não encontrou, tenta autenticar novamente aqui para garantir
|
return res.status(404).json({ error: 'Usuário não autenticado ou não encontrado no banco.' });
|
||||||
const user = await User.findOne({ id: (req as any).headers['x-user-id'] }); // Fallback opcional
|
|
||||||
if (!user) {
|
|
||||||
return res.status(404).json({ error: 'Usuário não autenticado ou não encontrado no banco.' });
|
|
||||||
}
|
|
||||||
return res.json(user);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json(req.appUser);
|
res.json(req.appUser);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('Error getting current user:', error);
|
console.error('Error getting current user:', error);
|
||||||
@@ -62,7 +76,12 @@ export const getAllUsers = async (req: Request, res: Response) => {
|
|||||||
const organizationId = req.headers['x-organization-id'] as string;
|
const organizationId = req.headers['x-organization-id'] as string;
|
||||||
|
|
||||||
if (!organizationId) {
|
if (!organizationId) {
|
||||||
return res.status(400).json({ error: 'Organização não selecionada.' });
|
const { data, error } = await supabase
|
||||||
|
.from('users')
|
||||||
|
.select('*');
|
||||||
|
|
||||||
|
if (error && error.code !== '42P01') throw error;
|
||||||
|
return res.json(data || []);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
@@ -70,7 +89,7 @@ export const getAllUsers = async (req: Request, res: Response) => {
|
|||||||
.select('*, users(*)')
|
.select('*, users(*)')
|
||||||
.eq('organization_id', organizationId);
|
.eq('organization_id', organizationId);
|
||||||
|
|
||||||
if (error) throw error;
|
if (error && error.code !== '42P01') throw error;
|
||||||
res.json(data || []);
|
res.json(data || []);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('Error getting users:', error);
|
console.error('Error getting users:', error);
|
||||||
@@ -142,12 +161,6 @@ export const heartbeat = async (req: AuthRequest, res: Response) => {
|
|||||||
|
|
||||||
export const getActiveUsers = async (req: AuthRequest, res: Response) => {
|
export const getActiveUsers = async (req: AuthRequest, res: Response) => {
|
||||||
try {
|
try {
|
||||||
const organizationId = req.headers['x-organization-id'] as string;
|
|
||||||
|
|
||||||
if (!organizationId) {
|
|
||||||
return res.status(400).json([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const twoMinutesAgo = new Date(Date.now() - 2 * 60 * 1000).toISOString();
|
const twoMinutesAgo = new Date(Date.now() - 2 * 60 * 1000).toISOString();
|
||||||
|
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
@@ -155,7 +168,7 @@ export const getActiveUsers = async (req: AuthRequest, res: Response) => {
|
|||||||
.select('id, email, name, last_seen_at')
|
.select('id, email, name, last_seen_at')
|
||||||
.gte('last_seen_at', twoMinutesAgo);
|
.gte('last_seen_at', twoMinutesAgo);
|
||||||
|
|
||||||
if (error) throw error;
|
if (error && error.code !== '42P01') throw error;
|
||||||
res.json(data || []);
|
res.json(data || []);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('Error getting active users:', error);
|
console.error('Error getting active users:', error);
|
||||||
@@ -178,4 +191,4 @@ export const deleteUser = async (req: Request, res: Response) => {
|
|||||||
console.error('Error deleting user:', error);
|
console.error('Error deleting user:', error);
|
||||||
res.status(500).json({ error: 'Erro ao remover membro.' });
|
res.status(500).json({ error: 'Erro ao remover membro.' });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1,45 +1,96 @@
|
|||||||
import { Inspection, findOneGpi, queryGpi } from '../lib/compat.js';
|
import { supabase } from '../config/supabase.js';
|
||||||
|
|
||||||
export const createInspection = async (data: any & { organizationId?: string, createdBy?: string }) => {
|
export const createInspection = async (data: any & { organizationId?: string, createdBy?: string }) => {
|
||||||
return await Inspection.create({
|
const { data: inspection, error } = await supabase
|
||||||
...data,
|
.from('inspections')
|
||||||
date: data.date ? new Date(data.date).toISOString() : null,
|
.insert({
|
||||||
organization_id: data.organizationId,
|
...data,
|
||||||
created_by: data.createdBy
|
date: data.date ? new Date(data.date).toISOString() : null,
|
||||||
});
|
organization_id: data.organizationId,
|
||||||
|
created_by: data.createdBy
|
||||||
|
})
|
||||||
|
.select()
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (error) throw error;
|
||||||
|
return inspection;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getInspectionsByProject = async (projectId: string, organizationId?: string) => {
|
export const getInspectionsByProject = async (projectId: string, organizationId?: string) => {
|
||||||
const filter: any = { project_id: projectId };
|
let query = supabase
|
||||||
|
.from('inspections')
|
||||||
|
.select('*')
|
||||||
|
.eq('project_id', projectId);
|
||||||
|
|
||||||
if (organizationId) {
|
if (organizationId) {
|
||||||
filter.organization_id = organizationId;
|
query = query.eq('organization_id', organizationId);
|
||||||
}
|
}
|
||||||
return await Inspection.find(filter);
|
|
||||||
|
const { data, error } = await query;
|
||||||
|
|
||||||
|
if (error && error.code !== '42P01') throw error;
|
||||||
|
return data || [];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getInspectionById = async (id: string) => {
|
export const getInspectionById = async (id: string) => {
|
||||||
return await Inspection.findById(id);
|
const { data, error } = await supabase
|
||||||
|
.from('inspections')
|
||||||
|
.select('*')
|
||||||
|
.eq('id', id)
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (error && error.code !== '42P01') throw error;
|
||||||
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateInspection = async (id: string, data: any) => {
|
export const updateInspection = async (id: string, data: any) => {
|
||||||
return await Inspection.findByIdAndUpdate(id, data);
|
const { data: inspection, error } = await supabase
|
||||||
|
.from('inspections')
|
||||||
|
.update(data)
|
||||||
|
.eq('id', id)
|
||||||
|
.select()
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (error) throw error;
|
||||||
|
return inspection;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const deleteInspection = async (id: string) => {
|
export const deleteInspection = async (id: string) => {
|
||||||
return await Inspection.findByIdAndDelete(id);
|
const { error } = await supabase
|
||||||
|
.from('inspections')
|
||||||
|
.delete()
|
||||||
|
.eq('id', id);
|
||||||
|
|
||||||
|
if (error) throw error;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getInspectionsByOrganization = async (organizationId: string) => {
|
export const getInspectionsByOrganization = async (organizationId: string) => {
|
||||||
return await Inspection.find({ organization_id: organizationId });
|
const { data, error } = await supabase
|
||||||
|
.from('inspections')
|
||||||
|
.select('*')
|
||||||
|
.eq('organization_id', organizationId);
|
||||||
|
|
||||||
|
if (error && error.code !== '42P01') throw error;
|
||||||
|
return data || [];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getInspectionStats = async (organizationId?: string) => {
|
export const getInspectionStats = async (organizationId?: string) => {
|
||||||
const filter = organizationId ? { organization_id: organizationId } : {};
|
let query = supabase.from('inspections').select('*');
|
||||||
const inspections = await Inspection.find(filter);
|
|
||||||
|
if (organizationId) {
|
||||||
|
query = query.eq('organization_id', organizationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data, error } = await query;
|
||||||
|
|
||||||
|
if (error && error.code !== '42P01') {
|
||||||
|
return { total: 0, inspections: [] };
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
total: inspections.length,
|
total: data?.length || 0,
|
||||||
inspections
|
inspections: data || []
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('✅ InspectionService loaded with compatibility');
|
console.log('✅ InspectionService loaded with Supabase');
|
||||||
Reference in New Issue
Block a user