create-crm-tmp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/create-crm-tmp.js +93 -0
- package/package.json +25 -0
- package/template/.prettierignore +33 -0
- package/template/.prettierrc.json +25 -0
- package/template/README.md +173 -0
- package/template/eslint.config.mjs +18 -0
- package/template/exemple-contacts.csv +11 -0
- package/template/next.config.ts +8 -0
- package/template/package.json +64 -0
- package/template/postcss.config.mjs +7 -0
- package/template/prisma/migrations/20251126144728_init/migration.sql +78 -0
- package/template/prisma/migrations/20251126155204_add_user_roles/migration.sql +5 -0
- package/template/prisma/migrations/20251128095126_add_company_info/migration.sql +19 -0
- package/template/prisma/migrations/20251128123321_add_smtp_config/migration.sql +22 -0
- package/template/prisma/migrations/20251128132303_add_status/migration.sql +23 -0
- package/template/prisma/migrations/20251201102207_add_user_active/migration.sql +75 -0
- package/template/prisma/migrations/20251201105507_add_email_signature/migration.sql +2 -0
- package/template/prisma/migrations/20251201151122_add_tasks/migration.sql +45 -0
- package/template/prisma/migrations/20251202111854_add_task_reminder/migration.sql +2 -0
- package/template/prisma/migrations/20251202135859_add_google_meet_integration/migration.sql +27 -0
- package/template/prisma/migrations/20251203103317_add_meta_lead_integration/migration.sql +20 -0
- package/template/prisma/migrations/20251203104002_add_google_ads_integration/migration.sql +18 -0
- package/template/prisma/migrations/20251203112122_add_google_sheet_integration/migration.sql +32 -0
- package/template/prisma/migrations/20251203153853_allow_multiple_integration_configs/migration.sql +20 -0
- package/template/prisma/migrations/20251205141705_update_user_roles/migration.sql +12 -0
- package/template/prisma/migrations/20251205150000_add_commercial_and_telepro_assignment/migration.sql +21 -0
- package/template/prisma/migrations/20251205160000_add_interaction_logging/migration.sql +11 -0
- package/template/prisma/migrations/20251208090314_add_automatic_interaction_types/migration.sql +12 -0
- package/template/prisma/migrations/20251208094843_mg/migration.sql +14 -0
- package/template/prisma/migrations/20251208100000_add_company_support/migration.sql +14 -0
- package/template/prisma/migrations/20251208110000_add_templates/migration.sql +26 -0
- package/template/prisma/migrations/20251208141304_add_video_conference_task_type/migration.sql +2 -0
- package/template/prisma/migrations/20251209104759_add_internal_note_to_task/migration.sql +2 -0
- package/template/prisma/migrations/20251209134803_add_company_field/migration.sql +2 -0
- package/template/prisma/migrations/20251209150000_rename_company_to_company_name/migration.sql +3 -0
- package/template/prisma/migrations/20251209150016_add_email_tracking/migration.sql +21 -0
- package/template/prisma/migrations/20251209155908_add_notify_contact_to_task/migration.sql +2 -0
- package/template/prisma/migrations/20251210110019_add_appointment_types/migration.sql +10 -0
- package/template/prisma/migrations/20251210113928_add_contact_files/migration.sql +26 -0
- package/template/prisma/migrations/20251212132339_add_custom_roles/migration.sql +24 -0
- package/template/prisma/migrations/20251215104448_add_file_interaction_types/migration.sql +11 -0
- package/template/prisma/migrations/20251215145616_add_closing_reasons/migration.sql +12 -0
- package/template/prisma/migrations/20251216140850_add_log_users/migration.sql +25 -0
- package/template/prisma/migrations/20251216151000_rename_perdu_to_ferme/migration.sql +8 -0
- package/template/prisma/migrations/20251216162318_add_column_mappings_to_google_sheet/migration.sql +2 -0
- package/template/prisma/migrations/20251216185127_add_workflows/migration.sql +80 -0
- package/template/prisma/migrations/20251216192237_add_scheduled_workflow_actions/migration.sql +32 -0
- package/template/prisma/migrations/migration_lock.toml +3 -0
- package/template/prisma/schema.prisma +582 -0
- package/template/prisma.config.ts +14 -0
- package/template/src/app/(auth)/invite/[token]/page.tsx +200 -0
- package/template/src/app/(auth)/layout.tsx +3 -0
- package/template/src/app/(auth)/reset-password/complete/page.tsx +213 -0
- package/template/src/app/(auth)/reset-password/page.tsx +146 -0
- package/template/src/app/(auth)/reset-password/verify/page.tsx +183 -0
- package/template/src/app/(auth)/signin/page.tsx +166 -0
- package/template/src/app/(dashboard)/agenda/page.tsx +3051 -0
- package/template/src/app/(dashboard)/automatisation/[id]/page.tsx +24 -0
- package/template/src/app/(dashboard)/automatisation/_components/workflow-editor.tsx +905 -0
- package/template/src/app/(dashboard)/automatisation/new/page.tsx +20 -0
- package/template/src/app/(dashboard)/automatisation/page.tsx +337 -0
- package/template/src/app/(dashboard)/closing/page.tsx +1052 -0
- package/template/src/app/(dashboard)/contacts/[id]/page.tsx +6028 -0
- package/template/src/app/(dashboard)/contacts/page.tsx +3713 -0
- package/template/src/app/(dashboard)/dashboard/page.tsx +186 -0
- package/template/src/app/(dashboard)/layout.tsx +30 -0
- package/template/src/app/(dashboard)/settings/page.tsx +4070 -0
- package/template/src/app/(dashboard)/templates/page.tsx +567 -0
- package/template/src/app/(dashboard)/users/list/page.tsx +507 -0
- package/template/src/app/(dashboard)/users/page.tsx +457 -0
- package/template/src/app/(dashboard)/users/permissions/page.tsx +181 -0
- package/template/src/app/(dashboard)/users/roles/page.tsx +434 -0
- package/template/src/app/api/audit-logs/route.ts +57 -0
- package/template/src/app/api/auth/[...all]/route.ts +4 -0
- package/template/src/app/api/auth/check-active/route.ts +31 -0
- package/template/src/app/api/auth/google/callback/route.ts +94 -0
- package/template/src/app/api/auth/google/disconnect/route.ts +32 -0
- package/template/src/app/api/auth/google/route.ts +34 -0
- package/template/src/app/api/auth/google/status/route.ts +32 -0
- package/template/src/app/api/closing-reasons/route.ts +27 -0
- package/template/src/app/api/contacts/[id]/files/[fileId]/route.ts +94 -0
- package/template/src/app/api/contacts/[id]/files/route.ts +269 -0
- package/template/src/app/api/contacts/[id]/interactions/[interactionId]/route.ts +91 -0
- package/template/src/app/api/contacts/[id]/interactions/route.ts +103 -0
- package/template/src/app/api/contacts/[id]/meet/route.ts +296 -0
- package/template/src/app/api/contacts/[id]/route.ts +322 -0
- package/template/src/app/api/contacts/[id]/send-email/route.ts +254 -0
- package/template/src/app/api/contacts/export/route.ts +270 -0
- package/template/src/app/api/contacts/import/route.ts +381 -0
- package/template/src/app/api/contacts/route.ts +283 -0
- package/template/src/app/api/dashboard/stats/route.ts +299 -0
- package/template/src/app/api/email/track/[id]/route.ts +68 -0
- package/template/src/app/api/integrations/google-sheet/sync/route.ts +526 -0
- package/template/src/app/api/invite/complete/route.ts +88 -0
- package/template/src/app/api/invite/validate/route.ts +55 -0
- package/template/src/app/api/reminders/route.ts +95 -0
- package/template/src/app/api/reset-password/complete/route.ts +73 -0
- package/template/src/app/api/reset-password/request/route.ts +84 -0
- package/template/src/app/api/reset-password/validate/route.ts +49 -0
- package/template/src/app/api/reset-password/verify/route.ts +74 -0
- package/template/src/app/api/roles/[id]/route.ts +183 -0
- package/template/src/app/api/roles/route.ts +140 -0
- package/template/src/app/api/send/route.ts +282 -0
- package/template/src/app/api/settings/change-password/route.ts +95 -0
- package/template/src/app/api/settings/closing-reasons/[id]/route.ts +84 -0
- package/template/src/app/api/settings/closing-reasons/route.ts +74 -0
- package/template/src/app/api/settings/company/route.ts +121 -0
- package/template/src/app/api/settings/google-ads/[id]/route.ts +117 -0
- package/template/src/app/api/settings/google-ads/route.ts +122 -0
- package/template/src/app/api/settings/google-sheet/[id]/route.ts +230 -0
- package/template/src/app/api/settings/google-sheet/auto-map/route.ts +196 -0
- package/template/src/app/api/settings/google-sheet/route.ts +254 -0
- package/template/src/app/api/settings/meta-leads/[id]/route.ts +123 -0
- package/template/src/app/api/settings/meta-leads/route.ts +132 -0
- package/template/src/app/api/settings/profile/route.ts +42 -0
- package/template/src/app/api/settings/smtp/route.ts +130 -0
- package/template/src/app/api/settings/smtp/test/route.ts +121 -0
- package/template/src/app/api/settings/statuses/[id]/route.ts +101 -0
- package/template/src/app/api/settings/statuses/route.ts +83 -0
- package/template/src/app/api/statuses/route.ts +25 -0
- package/template/src/app/api/tasks/[id]/attendees/route.ts +76 -0
- package/template/src/app/api/tasks/[id]/route.ts +728 -0
- package/template/src/app/api/tasks/meet/route.ts +240 -0
- package/template/src/app/api/tasks/route.ts +417 -0
- package/template/src/app/api/templates/[id]/route.ts +140 -0
- package/template/src/app/api/templates/route.ts +91 -0
- package/template/src/app/api/users/[id]/route.ts +168 -0
- package/template/src/app/api/users/list/route.ts +45 -0
- package/template/src/app/api/users/me/route.ts +48 -0
- package/template/src/app/api/users/route.ts +250 -0
- package/template/src/app/api/webhooks/google-ads/route.ts +208 -0
- package/template/src/app/api/webhooks/meta-leads/route.ts +258 -0
- package/template/src/app/api/workflows/[id]/route.ts +192 -0
- package/template/src/app/api/workflows/process/route.ts +293 -0
- package/template/src/app/api/workflows/route.ts +124 -0
- package/template/src/app/favicon.ico +0 -0
- package/template/src/app/globals.css +1416 -0
- package/template/src/app/layout.tsx +31 -0
- package/template/src/app/page.tsx +32 -0
- package/template/src/components/dashboard/activity-chart.tsx +67 -0
- package/template/src/components/dashboard/contacts-chart.tsx +63 -0
- package/template/src/components/dashboard/recent-activity.tsx +164 -0
- package/template/src/components/dashboard/sales-analytics-chart.tsx +81 -0
- package/template/src/components/dashboard/stat-card.tsx +61 -0
- package/template/src/components/dashboard/status-distribution-chart.tsx +45 -0
- package/template/src/components/dashboard/tasks-pie-chart.tsx +88 -0
- package/template/src/components/dashboard/top-contacts-list.tsx +129 -0
- package/template/src/components/dashboard/upcoming-tasks-list.tsx +126 -0
- package/template/src/components/editor.tsx +856 -0
- package/template/src/components/email-template.tsx +35 -0
- package/template/src/components/header.tsx +320 -0
- package/template/src/components/invitation-email-template.tsx +79 -0
- package/template/src/components/meet-cancellation-email-template.tsx +120 -0
- package/template/src/components/meet-confirmation-email-template.tsx +156 -0
- package/template/src/components/meet-update-email-template.tsx +209 -0
- package/template/src/components/page-header.tsx +61 -0
- package/template/src/components/reset-password-email-template.tsx +79 -0
- package/template/src/components/sidebar.tsx +294 -0
- package/template/src/components/skeleton.tsx +380 -0
- package/template/src/components/ui/commands.tsx +396 -0
- package/template/src/components/ui/components.tsx +150 -0
- package/template/src/components/ui/theme.tsx +5 -0
- package/template/src/components/view-as-banner.tsx +45 -0
- package/template/src/components/view-as-modal.tsx +186 -0
- package/template/src/contexts/mobile-menu-context.tsx +31 -0
- package/template/src/contexts/sidebar-context.tsx +107 -0
- package/template/src/contexts/task-reminder-context.tsx +239 -0
- package/template/src/contexts/view-as-context.tsx +84 -0
- package/template/src/hooks/use-user-role.ts +82 -0
- package/template/src/lib/audit-log.ts +45 -0
- package/template/src/lib/auth-client.ts +16 -0
- package/template/src/lib/auth.ts +35 -0
- package/template/src/lib/check-permission.ts +193 -0
- package/template/src/lib/contact-duplicate.ts +112 -0
- package/template/src/lib/contact-interactions.ts +371 -0
- package/template/src/lib/encryption.ts +99 -0
- package/template/src/lib/google-calendar.ts +300 -0
- package/template/src/lib/google-drive.ts +372 -0
- package/template/src/lib/permissions.ts +412 -0
- package/template/src/lib/prisma.ts +32 -0
- package/template/src/lib/roles.ts +120 -0
- package/template/src/lib/template-variables.ts +76 -0
- package/template/src/lib/utils.ts +46 -0
- package/template/src/lib/workflow-executor.ts +482 -0
- package/template/src/proxy.ts +91 -0
- package/template/tsconfig.json +34 -0
- package/template/vercel.json +8 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { auth } from '@/lib/auth';
|
|
3
|
+
import { prisma } from '@/lib/prisma';
|
|
4
|
+
|
|
5
|
+
// GET /api/templates/[id] - Récupérer un template spécifique
|
|
6
|
+
export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
|
7
|
+
try {
|
|
8
|
+
const session = await auth.api.getSession({
|
|
9
|
+
headers: request.headers,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
if (!session) {
|
|
13
|
+
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const { id } = await params;
|
|
17
|
+
|
|
18
|
+
const template = await prisma.template.findFirst({
|
|
19
|
+
where: {
|
|
20
|
+
id,
|
|
21
|
+
userId: session.user.id,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
if (!template) {
|
|
26
|
+
return NextResponse.json({ error: 'Template non trouvé' }, { status: 404 });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return NextResponse.json(template);
|
|
30
|
+
} catch (error: any) {
|
|
31
|
+
console.error('Erreur lors de la récupération du template:', error);
|
|
32
|
+
return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// PUT /api/templates/[id] - Mettre à jour un template
|
|
37
|
+
export async function PUT(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
|
38
|
+
try {
|
|
39
|
+
const session = await auth.api.getSession({
|
|
40
|
+
headers: request.headers,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
if (!session) {
|
|
44
|
+
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const { id } = await params;
|
|
48
|
+
const body = await request.json();
|
|
49
|
+
const { name, type, subject, content } = body;
|
|
50
|
+
|
|
51
|
+
// Vérifier que le template existe et appartient à l'utilisateur
|
|
52
|
+
const existing = await prisma.template.findFirst({
|
|
53
|
+
where: {
|
|
54
|
+
id,
|
|
55
|
+
userId: session.user.id,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
if (!existing) {
|
|
60
|
+
return NextResponse.json({ error: 'Template non trouvé' }, { status: 404 });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Validation
|
|
64
|
+
if (!name || !type || !content) {
|
|
65
|
+
return NextResponse.json(
|
|
66
|
+
{ error: 'Le nom, le type et le contenu sont requis' },
|
|
67
|
+
{ status: 400 },
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!['EMAIL', 'SMS', 'NOTE'].includes(type)) {
|
|
72
|
+
return NextResponse.json(
|
|
73
|
+
{ error: 'Type invalide. Doit être EMAIL, SMS ou NOTE' },
|
|
74
|
+
{ status: 400 },
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Pour EMAIL, le sujet est requis
|
|
79
|
+
if (type === 'EMAIL' && !subject) {
|
|
80
|
+
return NextResponse.json(
|
|
81
|
+
{ error: 'Le sujet est requis pour les templates EMAIL' },
|
|
82
|
+
{ status: 400 },
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const template = await prisma.template.update({
|
|
87
|
+
where: { id },
|
|
88
|
+
data: {
|
|
89
|
+
name,
|
|
90
|
+
type,
|
|
91
|
+
subject: type === 'EMAIL' ? subject : null,
|
|
92
|
+
content,
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
return NextResponse.json(template);
|
|
97
|
+
} catch (error: any) {
|
|
98
|
+
console.error('Erreur lors de la mise à jour du template:', error);
|
|
99
|
+
return NextResponse.json({ error: error.message || 'Erreur serveur' }, { status: 500 });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// DELETE /api/templates/[id] - Supprimer un template
|
|
104
|
+
export async function DELETE(
|
|
105
|
+
request: NextRequest,
|
|
106
|
+
{ params }: { params: Promise<{ id: string }> },
|
|
107
|
+
) {
|
|
108
|
+
try {
|
|
109
|
+
const session = await auth.api.getSession({
|
|
110
|
+
headers: request.headers,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
if (!session) {
|
|
114
|
+
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const { id } = await params;
|
|
118
|
+
|
|
119
|
+
// Vérifier que le template existe et appartient à l'utilisateur
|
|
120
|
+
const existing = await prisma.template.findFirst({
|
|
121
|
+
where: {
|
|
122
|
+
id,
|
|
123
|
+
userId: session.user.id,
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
if (!existing) {
|
|
128
|
+
return NextResponse.json({ error: 'Template non trouvé' }, { status: 404 });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
await prisma.template.delete({
|
|
132
|
+
where: { id },
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
return NextResponse.json({ success: true, message: 'Template supprimé avec succès' });
|
|
136
|
+
} catch (error: any) {
|
|
137
|
+
console.error('Erreur lors de la suppression du template:', error);
|
|
138
|
+
return NextResponse.json({ error: error.message || 'Erreur serveur' }, { status: 500 });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { auth } from '@/lib/auth';
|
|
3
|
+
import { prisma } from '@/lib/prisma';
|
|
4
|
+
|
|
5
|
+
// GET /api/templates - Récupérer tous les templates de l'utilisateur
|
|
6
|
+
export async function GET(request: NextRequest) {
|
|
7
|
+
try {
|
|
8
|
+
const session = await auth.api.getSession({
|
|
9
|
+
headers: request.headers,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
if (!session) {
|
|
13
|
+
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const { searchParams } = new URL(request.url);
|
|
17
|
+
const type = searchParams.get('type'); // EMAIL, SMS, NOTE
|
|
18
|
+
|
|
19
|
+
const where: any = {
|
|
20
|
+
userId: session.user.id,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
if (type) {
|
|
24
|
+
where.type = type;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const templates = await prisma.template.findMany({
|
|
28
|
+
where,
|
|
29
|
+
orderBy: { updatedAt: 'desc' },
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
return NextResponse.json(templates);
|
|
33
|
+
} catch (error: any) {
|
|
34
|
+
console.error('Erreur lors de la récupération des templates:', error);
|
|
35
|
+
return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// POST /api/templates - Créer un nouveau template
|
|
40
|
+
export async function POST(request: NextRequest) {
|
|
41
|
+
try {
|
|
42
|
+
const session = await auth.api.getSession({
|
|
43
|
+
headers: request.headers,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if (!session) {
|
|
47
|
+
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const body = await request.json();
|
|
51
|
+
const { name, type, subject, content } = body;
|
|
52
|
+
|
|
53
|
+
// Validation
|
|
54
|
+
if (!name || !type || !content) {
|
|
55
|
+
return NextResponse.json(
|
|
56
|
+
{ error: 'Le nom, le type et le contenu sont requis' },
|
|
57
|
+
{ status: 400 },
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!['EMAIL', 'SMS', 'NOTE'].includes(type)) {
|
|
62
|
+
return NextResponse.json(
|
|
63
|
+
{ error: 'Type invalide. Doit être EMAIL, SMS ou NOTE' },
|
|
64
|
+
{ status: 400 },
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Pour EMAIL, le sujet est requis
|
|
69
|
+
if (type === 'EMAIL' && !subject) {
|
|
70
|
+
return NextResponse.json(
|
|
71
|
+
{ error: 'Le sujet est requis pour les templates EMAIL' },
|
|
72
|
+
{ status: 400 },
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const template = await prisma.template.create({
|
|
77
|
+
data: {
|
|
78
|
+
name,
|
|
79
|
+
type,
|
|
80
|
+
subject: type === 'EMAIL' ? subject : null,
|
|
81
|
+
content,
|
|
82
|
+
userId: session.user.id,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return NextResponse.json(template, { status: 201 });
|
|
87
|
+
} catch (error: any) {
|
|
88
|
+
console.error('Erreur lors de la création du template:', error);
|
|
89
|
+
return NextResponse.json({ error: error.message || 'Erreur serveur' }, { status: 500 });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { prisma } from '@/lib/prisma';
|
|
3
|
+
import { checkPermission } from '@/lib/check-permission';
|
|
4
|
+
import { auth } from '@/lib/auth';
|
|
5
|
+
import { logAudit } from '@/lib/audit-log';
|
|
6
|
+
|
|
7
|
+
// GET /api/users/[id] - Récupérer un utilisateur spécifique
|
|
8
|
+
export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
|
9
|
+
try {
|
|
10
|
+
const session = await auth.api.getSession({
|
|
11
|
+
headers: request.headers,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
if (!session) {
|
|
15
|
+
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Vérifier que l'utilisateur a la permission de voir les utilisateurs
|
|
19
|
+
const hasPermission = await checkPermission('users.view');
|
|
20
|
+
if (!hasPermission) {
|
|
21
|
+
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
22
|
+
}
|
|
23
|
+
const { id } = await params;
|
|
24
|
+
|
|
25
|
+
const user = await prisma.user.findUnique({
|
|
26
|
+
where: { id },
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
if (!user) {
|
|
30
|
+
return NextResponse.json({ error: 'Utilisateur non trouvé' }, { status: 404 });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Retourner l'utilisateur avec le rôle
|
|
34
|
+
return NextResponse.json({
|
|
35
|
+
id: user.id,
|
|
36
|
+
name: user.name,
|
|
37
|
+
email: user.email,
|
|
38
|
+
role: user.role || 'USER',
|
|
39
|
+
emailVerified: user.emailVerified,
|
|
40
|
+
active: user.active,
|
|
41
|
+
image: user.image,
|
|
42
|
+
createdAt: user.createdAt,
|
|
43
|
+
updatedAt: user.updatedAt,
|
|
44
|
+
});
|
|
45
|
+
} catch (error: any) {
|
|
46
|
+
console.error('Erreur:', error);
|
|
47
|
+
|
|
48
|
+
if (error.message === 'Non authentifié') {
|
|
49
|
+
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (error.message === 'Permissions insuffisantes') {
|
|
53
|
+
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// PUT /api/users/[id] - Mettre à jour un utilisateur
|
|
61
|
+
export async function PUT(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
|
62
|
+
try {
|
|
63
|
+
const session = await auth.api.getSession({
|
|
64
|
+
headers: request.headers,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
if (!session) {
|
|
68
|
+
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Vérifier que l'utilisateur a la permission de modifier des utilisateurs
|
|
72
|
+
const hasPermission = await checkPermission('users.edit');
|
|
73
|
+
if (!hasPermission) {
|
|
74
|
+
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
75
|
+
}
|
|
76
|
+
const { id } = await params;
|
|
77
|
+
const body = await request.json();
|
|
78
|
+
const { name, customRoleId, active } = body;
|
|
79
|
+
|
|
80
|
+
// Vérifier que l'utilisateur existe
|
|
81
|
+
const existingUser = await prisma.user.findUnique({
|
|
82
|
+
where: { id },
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
if (!existingUser) {
|
|
86
|
+
return NextResponse.json({ error: 'Utilisateur non trouvé' }, { status: 404 });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Mettre à jour l'utilisateur
|
|
90
|
+
const updatedUser = await prisma.user.update({
|
|
91
|
+
where: { id },
|
|
92
|
+
data: {
|
|
93
|
+
...(name && { name }),
|
|
94
|
+
...(customRoleId !== undefined && { customRoleId: customRoleId || null }),
|
|
95
|
+
...(typeof active === 'boolean' && { active }),
|
|
96
|
+
},
|
|
97
|
+
include: {
|
|
98
|
+
customRole: {
|
|
99
|
+
select: {
|
|
100
|
+
id: true,
|
|
101
|
+
name: true,
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Récupérer les noms des profils pour les métadonnées
|
|
108
|
+
const getRoleName = async (roleId: string | null) => {
|
|
109
|
+
if (!roleId) return null;
|
|
110
|
+
const role = await prisma.customRole.findUnique({
|
|
111
|
+
where: { id: roleId },
|
|
112
|
+
select: { name: true },
|
|
113
|
+
});
|
|
114
|
+
return role?.name || null;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// Log d'audit : modification utilisateur
|
|
118
|
+
const changes: Record<string, { old: any; new: any }> = {};
|
|
119
|
+
if (name && name !== existingUser.name) {
|
|
120
|
+
changes.name = { old: existingUser.name, new: name };
|
|
121
|
+
}
|
|
122
|
+
if (customRoleId !== undefined && customRoleId !== existingUser.customRoleId) {
|
|
123
|
+
const oldRoleName = await getRoleName(existingUser.customRoleId);
|
|
124
|
+
const newRoleName = await getRoleName(customRoleId || null);
|
|
125
|
+
changes.profil = {
|
|
126
|
+
old: oldRoleName || 'Aucun',
|
|
127
|
+
new: newRoleName || 'Aucun',
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
if (typeof active === 'boolean' && active !== existingUser.active) {
|
|
131
|
+
changes.active = {
|
|
132
|
+
old: existingUser.active ? 'Actif' : 'Inactif',
|
|
133
|
+
new: active ? 'Actif' : 'Inactif',
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (Object.keys(changes).length > 0) {
|
|
138
|
+
await logAudit({
|
|
139
|
+
actorId: session.user.id,
|
|
140
|
+
targetUserId: updatedUser.id,
|
|
141
|
+
action: 'USER_UPDATED',
|
|
142
|
+
entityType: 'USER',
|
|
143
|
+
entityId: updatedUser.id,
|
|
144
|
+
metadata: {
|
|
145
|
+
changes,
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Retourner l'utilisateur avec le profil
|
|
151
|
+
return NextResponse.json({
|
|
152
|
+
id: updatedUser.id,
|
|
153
|
+
name: updatedUser.name,
|
|
154
|
+
email: updatedUser.email,
|
|
155
|
+
role: updatedUser.role || 'USER',
|
|
156
|
+
customRoleId: updatedUser.customRoleId,
|
|
157
|
+
customRole: updatedUser.customRole,
|
|
158
|
+
emailVerified: updatedUser.emailVerified,
|
|
159
|
+
active: updatedUser.active,
|
|
160
|
+
updatedAt: updatedUser.updatedAt,
|
|
161
|
+
});
|
|
162
|
+
} catch (error: any) {
|
|
163
|
+
console.error('Erreur:', error);
|
|
164
|
+
return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 });
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// NOTE: L'API DELETE n'est plus utilisée : les comptes sont désormais désactivés via le booléen `active`.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { auth } from '@/lib/auth';
|
|
3
|
+
import { prisma } from '@/lib/prisma';
|
|
4
|
+
import { checkPermission } from '@/lib/check-permission';
|
|
5
|
+
|
|
6
|
+
// GET /api/users/list - Récupérer la liste des utilisateurs avec leurs profils (pour les admins)
|
|
7
|
+
export async function GET(request: NextRequest) {
|
|
8
|
+
try {
|
|
9
|
+
const session = await auth.api.getSession({
|
|
10
|
+
headers: request.headers,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
if (!session) {
|
|
14
|
+
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Vérifier que l'utilisateur a la permission de gérer les rôles (admin)
|
|
18
|
+
const hasPermission = await checkPermission('users.manage_roles');
|
|
19
|
+
if (!hasPermission) {
|
|
20
|
+
return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Récupérer tous les utilisateurs avec leurs profils
|
|
24
|
+
const users = await prisma.user.findMany({
|
|
25
|
+
select: {
|
|
26
|
+
id: true,
|
|
27
|
+
name: true,
|
|
28
|
+
email: true,
|
|
29
|
+
customRole: {
|
|
30
|
+
select: {
|
|
31
|
+
id: true,
|
|
32
|
+
name: true,
|
|
33
|
+
permissions: true,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
orderBy: { name: 'asc' },
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
return NextResponse.json(users);
|
|
41
|
+
} catch (error: any) {
|
|
42
|
+
console.error('Erreur lors de la récupération des utilisateurs:', error);
|
|
43
|
+
return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
import { auth } from '@/lib/auth';
|
|
3
|
+
import { prisma } from '@/lib/prisma';
|
|
4
|
+
|
|
5
|
+
// GET /api/users/me - Récupérer les informations de l'utilisateur actuel (y compris le rôle)
|
|
6
|
+
export async function GET(request: NextRequest) {
|
|
7
|
+
try {
|
|
8
|
+
const session = await auth.api.getSession({
|
|
9
|
+
headers: request.headers,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
if (!session) {
|
|
13
|
+
return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Récupérer l'utilisateur avec son profil depuis la base de données
|
|
17
|
+
const user = await prisma.user.findUnique({
|
|
18
|
+
where: { id: session.user.id },
|
|
19
|
+
include: {
|
|
20
|
+
customRole: {
|
|
21
|
+
select: {
|
|
22
|
+
id: true,
|
|
23
|
+
name: true,
|
|
24
|
+
permissions: true,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
if (!user) {
|
|
31
|
+
return NextResponse.json({ error: 'Utilisateur non trouvé' }, { status: 404 });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Retourner l'utilisateur avec son profil et ses permissions
|
|
35
|
+
return NextResponse.json({
|
|
36
|
+
id: user.id,
|
|
37
|
+
name: user.name,
|
|
38
|
+
email: user.email,
|
|
39
|
+
role: 'USER', // Tous les utilisateurs ont le rôle USER
|
|
40
|
+
customRole: user.customRole,
|
|
41
|
+
emailVerified: user.emailVerified,
|
|
42
|
+
image: user.image,
|
|
43
|
+
});
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error('Erreur:', error);
|
|
46
|
+
return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 });
|
|
47
|
+
}
|
|
48
|
+
}
|