Compare commits
2 Commits
b8648fb5f7
...
b0139b6cab
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b0139b6cab | ||
|
|
ede38e80e4 |
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useDictionary } from "@/components/DictionaryContext";
|
||||
|
||||
interface User {
|
||||
@@ -19,6 +20,7 @@ export default function UsersPage() {
|
||||
const [search, setSearch] = useState("");
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [editingUser, setEditingUser] = useState<User | null>(null);
|
||||
const { data: session } = useSession();
|
||||
|
||||
// Form state
|
||||
const [formData, setFormData] = useState({
|
||||
@@ -242,6 +244,7 @@ export default function UsersPage() {
|
||||
<input className="input" type="password" value={formData.password} onChange={e => setFormData({...formData, password: e.target.value})} required={!editingUser} />
|
||||
</div>
|
||||
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14 }}>
|
||||
{session?.user?.role === "SUPER_ADMIN" ? (
|
||||
<div>
|
||||
<label className="label">Rol</label>
|
||||
<select className="input" value={formData.role} onChange={e => setFormData({...formData, role: e.target.value})}>
|
||||
@@ -249,15 +252,28 @@ export default function UsersPage() {
|
||||
<option value="DOMAIN_ADMIN">Domain Admin</option>
|
||||
</select>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<label className="label">Rol</label>
|
||||
<input className="input" value="Domain Admin" disabled />
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<label className="label">Telegram ID</label>
|
||||
<input className="input" placeholder="Örn: 5009005027" value={formData.telegramId} onChange={e => setFormData({...formData, telegramId: e.target.value})} />
|
||||
</div>
|
||||
</div>
|
||||
{session?.user?.role === "SUPER_ADMIN" ? (
|
||||
<div>
|
||||
<label className="label">İzinli Domainler (Virgülle ayırın, tümü için *)</label>
|
||||
<input className="input" placeholder="domain1.com, domain2.com" value={formData.domains} onChange={e => setFormData({...formData, domains: e.target.value})} />
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<label className="label">İzinli Domainler</label>
|
||||
<input className="input" value={session?.user?.domains?.join(", ")} disabled />
|
||||
</div>
|
||||
)}
|
||||
<div style={{ display: "flex", gap: 10, marginTop: 10 }}>
|
||||
<button type="button" className="btn btn-ghost" style={{ flex: 1 }} onClick={() => setIsModalOpen(false)}>İptal</button>
|
||||
<button type="submit" className="btn btn-primary" style={{ flex: 1 }} disabled={saving}>
|
||||
|
||||
@@ -7,22 +7,44 @@ export async function PATCH(req: NextRequest, { params }: { params: Promise<{ id
|
||||
const session = await auth();
|
||||
const { id } = await params;
|
||||
|
||||
if (!session || session.user.role !== "SUPER_ADMIN") {
|
||||
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
||||
if (!session) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
const userRole = session.user.role;
|
||||
const adminDomains = session.user.domains || [];
|
||||
|
||||
try {
|
||||
// Mevcut kullanıcıyı kontrol et
|
||||
const existingUser = await prisma.user.findUnique({ where: { id } });
|
||||
if (!existingUser) return NextResponse.json({ error: "User not found" }, { status: 404 });
|
||||
|
||||
// Güvenlik Kontrolü: Domain admin sadece kendi domainindeki kullanıcıyı güncelleyebilir
|
||||
if (userRole !== "SUPER_ADMIN") {
|
||||
const hasAccess = existingUser.domains.some(d => adminDomains.includes(d));
|
||||
if (!hasAccess) return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
||||
}
|
||||
|
||||
const body = await req.json();
|
||||
const { name, email, password, role, domains, telegramId } = body;
|
||||
|
||||
let finalDomains = domains;
|
||||
let finalRole = role;
|
||||
|
||||
// Güvenlik: Domain admin yetki yükseltemez veya domain değiştiremez
|
||||
if (userRole !== "SUPER_ADMIN") {
|
||||
finalDomains = adminDomains; // Kendi domainlerine kilitler
|
||||
finalRole = "DOMAIN_ADMIN";
|
||||
}
|
||||
|
||||
const user = await prisma.user.update({
|
||||
where: { id },
|
||||
data: {
|
||||
name,
|
||||
email: email?.toLowerCase(),
|
||||
password,
|
||||
role,
|
||||
domains,
|
||||
role: finalRole,
|
||||
domains: finalDomains,
|
||||
telegramId,
|
||||
},
|
||||
});
|
||||
@@ -38,11 +60,23 @@ export async function DELETE(req: NextRequest, { params }: { params: Promise<{ i
|
||||
const session = await auth();
|
||||
const { id } = await params;
|
||||
|
||||
if (!session || session.user.role !== "SUPER_ADMIN") {
|
||||
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
||||
if (!session) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
const userRole = session.user.role;
|
||||
const adminDomains = session.user.domains || [];
|
||||
|
||||
try {
|
||||
const existingUser = await prisma.user.findUnique({ where: { id } });
|
||||
if (!existingUser) return NextResponse.json({ error: "User not found" }, { status: 404 });
|
||||
|
||||
// Güvenlik Kontrolü
|
||||
if (userRole !== "SUPER_ADMIN") {
|
||||
const hasAccess = existingUser.domains.some(d => adminDomains.includes(d));
|
||||
if (!hasAccess) return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
||||
}
|
||||
|
||||
await prisma.user.delete({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
@@ -5,13 +5,30 @@ import { prisma } from "@/lib/prisma";
|
||||
// GET /api/users — list all users
|
||||
export async function GET() {
|
||||
const session = await auth();
|
||||
if (!session || session.user.role !== "SUPER_ADMIN") {
|
||||
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
||||
if (!session) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
const users = await prisma.user.findMany({
|
||||
const userRole = session.user.role;
|
||||
const userDomains = session.user.domains || [];
|
||||
|
||||
let users;
|
||||
if (userRole === "SUPER_ADMIN") {
|
||||
// Super admin her şeyi görür
|
||||
users = await prisma.user.findMany({
|
||||
orderBy: { createdAt: "asc" },
|
||||
});
|
||||
} else {
|
||||
// Domain admin sadece kendi domainlerine dokunan kullanıcıları görür
|
||||
users = await prisma.user.findMany({
|
||||
where: {
|
||||
domains: {
|
||||
hasSome: userDomains
|
||||
}
|
||||
},
|
||||
orderBy: { createdAt: "asc" },
|
||||
});
|
||||
}
|
||||
|
||||
return NextResponse.json(users);
|
||||
}
|
||||
@@ -19,21 +36,34 @@ export async function GET() {
|
||||
// POST /api/users — create a new user
|
||||
export async function POST(req: NextRequest) {
|
||||
const session = await auth();
|
||||
if (!session || session.user.role !== "SUPER_ADMIN") {
|
||||
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
||||
if (!session) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
const userRole = session.user.role;
|
||||
const adminDomains = session.user.domains || [];
|
||||
|
||||
try {
|
||||
const body = await req.json();
|
||||
const { name, email, password, role, domains, telegramId } = body;
|
||||
|
||||
let finalDomains = domains || [];
|
||||
let finalRole = role || "DOMAIN_ADMIN";
|
||||
|
||||
// Güvenlik: Domain admin yetkisini aşamaz
|
||||
if (userRole !== "SUPER_ADMIN") {
|
||||
// Eğer domain admin ise, yeni kullanıcıya sadece kendi domainlerini verebilir
|
||||
finalDomains = adminDomains;
|
||||
finalRole = "DOMAIN_ADMIN"; // Başka bir super admin oluşturamaz
|
||||
}
|
||||
|
||||
const user = await prisma.user.create({
|
||||
data: {
|
||||
name,
|
||||
email: email.toLowerCase(),
|
||||
password,
|
||||
role: role || "DOMAIN_ADMIN",
|
||||
domains: domains || [],
|
||||
role: finalRole,
|
||||
domains: finalDomains,
|
||||
telegramId,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
import { sendTelegramNotification } from '@/lib/notifications';
|
||||
import { sendWA } from '@/lib/whatsapp';
|
||||
|
||||
// Bu kısım normalde .env içinde olmalı
|
||||
const WEBHOOK_SECRET = process.env.WEBHOOK_SIGNAL_SECRET || 'besiktasK1903*';
|
||||
@@ -74,7 +75,14 @@ export async function POST(request: Request) {
|
||||
"" // Analiz bilgisini kaldırdık
|
||||
);
|
||||
|
||||
// 4. Bildirim Logu
|
||||
// 4. Bildirim Gönder (WhatsApp)
|
||||
// Şu an için varsayılan numaraya gönderiyoruz, ilerde User modeline alan eklenebilir.
|
||||
const waNumber = process.env.DEFAULT_WHATSAPP_NUMBER || '905543765103';
|
||||
const waMessage = `📩 *Yeni E-posta*\n\n*Gönderen:* ${mailData.from}\n*Konu:* ${mailData.subject}\n*Alıcı:* ${to}\n\n_AyrisMail Central_`;
|
||||
|
||||
await sendWA(waNumber, waMessage);
|
||||
|
||||
// 5. Bildirim Logu
|
||||
await prisma.notificationLog.create({
|
||||
data: {
|
||||
mailbox: to,
|
||||
|
||||
@@ -24,8 +24,8 @@ export default function Sidebar({ dict, lang }: { dict: any; lang: string }) {
|
||||
section: dict.sidebar?.management || "YÖNETİM",
|
||||
items: [
|
||||
{ href: `/${lang}/dashboard/domains`, label: dict.domains?.title || "Domainler", icon: GlobeIcon, roles: ["SUPER_ADMIN"] },
|
||||
{ href: `/${lang}/dashboard/users`, label: dict.sidebar?.users || "Kullanıcılar", icon: UsersIcon, roles: ["SUPER_ADMIN"] },
|
||||
{ href: `/${lang}/dashboard/mappings`, label: dict.sidebar?.mappings || "Eşleştirmeler", icon: LinkIcon, roles: ["SUPER_ADMIN"] },
|
||||
{ href: `/${lang}/dashboard/users`, label: dict.sidebar?.users || "Kullanıcılar", icon: UsersIcon, roles: ["SUPER_ADMIN", "DOMAIN_ADMIN"] },
|
||||
{ href: `/${lang}/dashboard/mappings`, label: dict.sidebar?.mappings || "Eşleştirmeler", icon: LinkIcon, roles: ["SUPER_ADMIN", "DOMAIN_ADMIN"] },
|
||||
{ href: `/${lang}/dashboard/mailboxes`, label: dict.sidebar?.mailboxes || "Mail Hesapları", icon: MailIcon, roles: ["SUPER_ADMIN", "DOMAIN_ADMIN"] },
|
||||
{ href: `/${lang}/dashboard/logs`, label: dict.sidebar?.logs || "Loglar", icon: ListIcon, roles: ["SUPER_ADMIN"] },
|
||||
],
|
||||
|
||||
35
lib/whatsapp.ts
Normal file
35
lib/whatsapp.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* lib/whatsapp.ts
|
||||
* Centralized service to send WhatsApp messages via AyrisTech WhatsApp Worker.
|
||||
*/
|
||||
|
||||
export async function sendWA(to: string, message: string, userId: string = 'mustafa_yildiz') {
|
||||
try {
|
||||
const workerUrl = process.env.WHATSAPP_WORKER_URL;
|
||||
const secret = process.env.WHATSAPP_SECRET;
|
||||
|
||||
if (!workerUrl || !secret) {
|
||||
console.error('[WhatsApp] Missing environment variables (URL or Secret)');
|
||||
return { success: false, error: 'Config missing' };
|
||||
}
|
||||
|
||||
const response = await fetch(`${workerUrl}/send-message`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
secret,
|
||||
userId,
|
||||
to,
|
||||
message
|
||||
}),
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
if (!response.ok) throw new Error(data.error || 'Mesaj gönderilemedi');
|
||||
|
||||
return { success: true, data };
|
||||
} catch (error) {
|
||||
console.error('WhatsApp Error:', error);
|
||||
return { success: false, error: error instanceof Error ? error.message : 'Bilinmeyen hata' };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user