Updated ERP with new modules and features
This commit is contained in:
42
app/Http/Controllers/ActivityLogController.php
Normal file
42
app/Http/Controllers/ActivityLogController.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\ActivityLog;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ActivityLogController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$query = ActivityLog::with(['user', 'loggable'])->latest();
|
||||
|
||||
// 1. Filter by Action
|
||||
if ($request->filled('action_type')) {
|
||||
$query->where('action', $request->action_type);
|
||||
}
|
||||
|
||||
// 2. Filter by Date Range
|
||||
if ($request->filled('date_from')) {
|
||||
$query->whereDate('created_at', '>=', $request->date_from);
|
||||
}
|
||||
if ($request->filled('date_to')) {
|
||||
$query->whereDate('created_at', '<=', $request->date_to);
|
||||
}
|
||||
|
||||
// 3. Search by User or Entity Type
|
||||
if ($request->filled('search')) {
|
||||
$searchTerm = $request->search;
|
||||
$query->where(function ($q) use ($searchTerm) {
|
||||
$q->whereHas('user', function ($userQuery) use ($searchTerm) {
|
||||
$userQuery->where('name', 'like', '%' . $searchTerm . '%');
|
||||
})
|
||||
->orWhere('loggable_type', 'like', '%' . $searchTerm . '%');
|
||||
});
|
||||
}
|
||||
|
||||
$logs = $query->paginate(20)->withQueryString();
|
||||
|
||||
return view('activity-logs.index', compact('logs'));
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,10 @@ class DailyReportController extends Controller
|
||||
->where('report_date', $request->report_date)
|
||||
->delete();
|
||||
|
||||
$managementEmails = ['samuel@click-mobile.com'];
|
||||
$managementEmails = ['kwesi@click-mobile.com'];
|
||||
// $managementEmails = User::whereIn('group', ['directors'])
|
||||
// ->pluck('email')
|
||||
// ->toArray();
|
||||
Mail::to($managementEmails)->send(new DailyReportSubmitted($report, auth()->user()));
|
||||
|
||||
return response()->json([
|
||||
|
||||
@@ -135,7 +135,7 @@ class LeaveRequestController extends Controller
|
||||
$leaveRequest->update([
|
||||
'status' => $validated['status'],
|
||||
'admin_remarks' => $validated['admin_remarks'],
|
||||
'approved_by' => auth()->id(), // Track who approved it
|
||||
'approved_by' => auth()->id(),
|
||||
]);
|
||||
|
||||
return response()->json(['success' => true, 'message' => 'Leave status updated successfully!']);
|
||||
|
||||
64
app/Http/Controllers/ManagementDashboardController.php
Normal file
64
app/Http/Controllers/ManagementDashboardController.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\StaffMember;
|
||||
use App\Models\LeaveRequest;
|
||||
use Illuminate\Http\Request;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class ManagementDashboardController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
// 1. Fetch Pending Leaves (Adjust status/model names to match your schema)
|
||||
$pendingLeaves = LeaveRequest::with('staff')
|
||||
->where('status', 'Pending')
|
||||
->orderBy('created_at', 'asc')
|
||||
->get();
|
||||
|
||||
|
||||
$startDate = $request->input('start_date', Carbon::now()->startOfMonth()->format('Y-m-d'));
|
||||
$endDate = $request->input('end_date', Carbon::now()->endOfMonth()->format('Y-m-d'));
|
||||
|
||||
$staffReports = StaffMember::withCount(['dailyReports' => function ($query) use ($startDate, $endDate) {
|
||||
$query->whereBetween('report_date', [$startDate, $endDate]);
|
||||
}])
|
||||
->orderBy('name')
|
||||
->get();
|
||||
|
||||
return view('management.index', compact('pendingLeaves', 'staffReports', 'startDate', 'endDate'));
|
||||
}
|
||||
|
||||
public function updateLeaveStatus(Request $request, $id)
|
||||
{
|
||||
$request->validate(['status' => 'required|in:Approved,Rejected']);
|
||||
|
||||
$leave = LeaveRequest::findOrFail($id);
|
||||
if ($request['status'] === 'Approved' && $leave->status !== 'Approved') {
|
||||
if ($leave->leave_type === 'Annual') {
|
||||
$staff = $leave->staff;
|
||||
$staff->annual_leave_balance -= $leave->total_days;
|
||||
$staff->save();
|
||||
}
|
||||
}
|
||||
|
||||
if ($request['status'] === 'Rejected' && $leave->status === 'Rejected') {
|
||||
if ($leave->leave_type === 'Annual') {
|
||||
$staff = $leave->staff;
|
||||
$staff->annual_leave_balance += $leave->total_days;
|
||||
$staff->save();
|
||||
}
|
||||
}
|
||||
|
||||
$leave->update([
|
||||
'status' => strtoupper($request['status']),
|
||||
'admin_remarks' => 'N/A',
|
||||
'approved_by' => auth()->id(),
|
||||
]);
|
||||
|
||||
// $leave->update(['status' => $request->status]);
|
||||
|
||||
return response()->json(['success' => true, 'message' => "Leave request {$request->status} successfully."]);
|
||||
}
|
||||
}
|
||||
@@ -79,6 +79,8 @@ class NetworkOperatorsController extends Controller
|
||||
$request->validate([
|
||||
'name' => 'required|string|max:200',
|
||||
'country' => 'required|string',
|
||||
'contract_auto_renew' => 'required|boolean',
|
||||
'next_renewal_date' => 'nullable|required_if:contract_auto_renew,0|date',
|
||||
]);
|
||||
|
||||
$operator->update([
|
||||
@@ -98,6 +100,10 @@ class NetworkOperatorsController extends Controller
|
||||
'rate_type' => $request->rate_type,
|
||||
'payment_terms' => $request->payment_terms,
|
||||
'connection_type' => $request->connection_type,
|
||||
'contract_type' => $request->contract_type,
|
||||
'contract_validity' => $request->contract_validity,
|
||||
'contract_auto_renew' => $request->contract_auto_renew,
|
||||
'next_renewal_date' => $request->contract_auto_renew ? null : $request->next_renewal_date,
|
||||
]);
|
||||
|
||||
return response()->json(['message' => 'MNO Partner updated successfully!']);
|
||||
@@ -112,7 +118,8 @@ class NetworkOperatorsController extends Controller
|
||||
public function show($id)
|
||||
{
|
||||
$operator = NetworkOperator::findOrFail($id);
|
||||
return view('network_operators.show', compact('operator'));
|
||||
$staffMembers = \App\Models\StaffMember::orderBy('name')->get();
|
||||
return view('network_operators.show', compact('operator', 'staffMembers'));
|
||||
}
|
||||
|
||||
public function storeConnection(Request $request, $operatorId)
|
||||
@@ -171,5 +178,45 @@ class NetworkOperatorsController extends Controller
|
||||
return redirect()->back()->with('success', 'Connection detail removed.');
|
||||
}
|
||||
|
||||
public function storeDocument(Request $request, $operatorId)
|
||||
{
|
||||
$request->validate([
|
||||
'document_names' => 'required|array',
|
||||
'document_names.*' => 'required|string|max:255',
|
||||
'documents' => 'required|array',
|
||||
'documents.*' => 'required|file|mimes:pdf,doc,docx,xlsx,xls,png,jpg,jpeg|max:10240', // 10MB limit
|
||||
]);
|
||||
|
||||
foreach ($request->document_names as $index => $name) {
|
||||
if ($request->hasFile("documents.$index")) {
|
||||
$filePath = $request->file("documents.$index")->store('mno_documents', 'public');
|
||||
|
||||
\App\Models\NetworkOperatorDocument::create([
|
||||
'network_operator_id' => $operatorId,
|
||||
'name' => $name,
|
||||
'file_path' => $filePath,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'Documents uploaded successfully!'
|
||||
]);
|
||||
}
|
||||
|
||||
public function destroyDocument($id)
|
||||
{
|
||||
$document = \App\Models\NetworkOperatorDocument::findOrFail($id);
|
||||
|
||||
// Optionally delete the file from storage
|
||||
if (\Illuminate\Support\Facades\Storage::disk('public')->exists($document->file_path)) {
|
||||
\Illuminate\Support\Facades\Storage::disk('public')->delete($document->file_path);
|
||||
}
|
||||
|
||||
$document->delete();
|
||||
|
||||
return redirect()->back()->with('success', 'Document deleted successfully.');
|
||||
}
|
||||
|
||||
}
|
||||
63
app/Http/Controllers/ServerController.php
Normal file
63
app/Http/Controllers/ServerController.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ServerController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$servers = Server::orderBy('name', 'asc')->paginate(15);
|
||||
return view('servers.index', compact('servers'));
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'ip_address' => 'required|ip',
|
||||
'os_version' => 'nullable|string|max:255',
|
||||
'username' => 'required|string|max:255',
|
||||
'password' => 'required|string',
|
||||
'services' => 'nullable|array',
|
||||
'status' => 'required|in:Active,Inactive,Maintenance',
|
||||
]);
|
||||
|
||||
Server::create($validated);
|
||||
|
||||
return response()->json(['success' => true, 'message' => 'Server added successfully.']);
|
||||
}
|
||||
|
||||
// Return server data for the edit modal
|
||||
public function show($id)
|
||||
{
|
||||
$server = Server::findOrFail($id);
|
||||
return response()->json($server);
|
||||
}
|
||||
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$server = Server::findOrFail($id);
|
||||
|
||||
$validated = $request->validate([
|
||||
'name' => 'required|string|max:255',
|
||||
'ip_address' => 'required|ip',
|
||||
'os_version' => 'nullable|string|max:255',
|
||||
'username' => 'required|string|max:255',
|
||||
'password' => 'nullable|string',
|
||||
'services' => 'nullable|array',
|
||||
'status' => 'required|in:Active,Inactive,Maintenance',
|
||||
]);
|
||||
|
||||
// If password field is empty, remove it from the array so we don't overwrite the existing one
|
||||
if (empty($validated['password'])) {
|
||||
unset($validated['password']);
|
||||
}
|
||||
|
||||
$server->update($validated);
|
||||
|
||||
return response()->json(['success' => true, 'message' => 'Server updated successfully.']);
|
||||
}
|
||||
}
|
||||
70
app/Http/Controllers/ServerCredentialController.php
Normal file
70
app/Http/Controllers/ServerCredentialController.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ServerCredentialController extends Controller
|
||||
{
|
||||
// Step 1: Generate and Email OTP
|
||||
public function requestOtp(Request $request, $serverId)
|
||||
{
|
||||
$server = Server::findOrFail($serverId);
|
||||
$user = auth()->user();
|
||||
|
||||
// Generate a 6-digit OTP
|
||||
$otp = random_int(100000, 999999);
|
||||
|
||||
// Store in cache for 5 minutes tied specifically to this user and server
|
||||
$cacheKey = "otp_{$user->id}_server_{$server->id}";
|
||||
Cache::put($cacheKey, $otp, now()->addMinutes(5));
|
||||
|
||||
// Dispatch Email (You can replace this with a proper Mailable later)
|
||||
Mail::raw("Your OTP to view the credentials for {$server->name} ({$server->ip_address}) is: {$otp}. This code expires in 5 minutes.", function ($message) use ($user) {
|
||||
$message->to($user->email)
|
||||
->subject('Server Access Verification Code');
|
||||
});
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'An OTP has been sent to your email.'
|
||||
]);
|
||||
}
|
||||
|
||||
// Step 2: Verify OTP and Release Password
|
||||
public function verifyOtp(Request $request, $serverId)
|
||||
{
|
||||
$request->validate(['otp' => 'required|numeric']);
|
||||
|
||||
$user = auth()->user();
|
||||
$server = Server::findOrFail($serverId);
|
||||
$cacheKey = "otp_{$user->id}_server_{$server->id}";
|
||||
|
||||
if (Cache::get($cacheKey) == $request->otp) {
|
||||
// Clear the OTP so it cannot be reused
|
||||
Cache::forget($cacheKey);
|
||||
|
||||
// Log the access in the audit trail
|
||||
DB::table('password_access_audits')->insert([
|
||||
'user_id' => $user->id,
|
||||
'server_id' => $server->id,
|
||||
'ip_address' => $request->ip(),
|
||||
'user_agent' => $request->userAgent(),
|
||||
'accessed_at' => now(),
|
||||
]);
|
||||
|
||||
// Return the decrypted password (Laravel handles decryption automatically via the model cast)
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'username' => $server->username,
|
||||
'password' => $server->password
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json(['success' => false, 'message' => 'Invalid or expired OTP.'], 422);
|
||||
}
|
||||
}
|
||||
166
app/Http/Controllers/VpnConfigController.php
Normal file
166
app/Http/Controllers/VpnConfigController.php
Normal file
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\VpnConfig;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class VpnConfigController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$vpns = VpnConfig::orderBy('partner_name')->paginate(15);
|
||||
return view('vpns.index', compact('vpns'));
|
||||
}
|
||||
|
||||
// Handle Add Form Submission
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'partner_name' => 'required|string|max:255',
|
||||
'status' => 'required|in:Active,Inactive',
|
||||
'local_ip' => 'required|ip',
|
||||
'peer_ip' => 'required|ip',
|
||||
'encryption_domain_local' => 'nullable|string|max:255',
|
||||
'encryption_domain_remote' => 'nullable|string|max:255',
|
||||
'preshared_key' => 'required|string',
|
||||
'local_device' => 'nullable|string|max:255',
|
||||
'peer_device' => 'nullable|string|max:255',
|
||||
'ike_version' => 'required|integer',
|
||||
'phase1_encryption' => 'required|string|max:50',
|
||||
'phase1_hash' => 'required|string|max:50',
|
||||
'phase1_dh_group' => 'required|string|max:50',
|
||||
'phase1_lifetime' => 'required|integer',
|
||||
'phase2_encryption' => 'required|string|max:50',
|
||||
'phase2_hash' => 'required|string|max:50',
|
||||
'phase2_dh_group' => 'nullable|string|max:50',
|
||||
'phase2_lifetime' => 'required|integer',
|
||||
'pfs_enabled' => 'boolean',
|
||||
]);
|
||||
|
||||
VpnConfig::create($validated);
|
||||
|
||||
return response()->json(['success' => true, 'message' => 'VPN configuration added successfully.']);
|
||||
}
|
||||
|
||||
// Fetch data for the Edit Modal
|
||||
public function show($id)
|
||||
{
|
||||
$vpn = VpnConfig::findOrFail($id);
|
||||
|
||||
// Hide the pre-shared key from being sent back in the JSON response for security
|
||||
$vpn->makeHidden('preshared_key');
|
||||
|
||||
return response()->json($vpn);
|
||||
}
|
||||
|
||||
// Handle Edit Form Submission
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$vpn = VpnConfig::findOrFail($id);
|
||||
|
||||
$validated = $request->validate([
|
||||
'partner_name' => 'required|string|max:255',
|
||||
'status' => 'required|in:Active,Inactive',
|
||||
'local_ip' => 'required|ip',
|
||||
'peer_ip' => 'required|ip',
|
||||
'encryption_domain_local' => 'nullable|string|max:255',
|
||||
'encryption_domain_remote' => 'nullable|string|max:255',
|
||||
'preshared_key' => 'nullable|string',
|
||||
'local_device' => 'nullable|string|max:255',
|
||||
'peer_device' => 'nullable|string|max:255',
|
||||
'ike_version' => 'required|integer',
|
||||
'phase1_encryption' => 'required|string|max:50',
|
||||
'phase1_hash' => 'required|string|max:50',
|
||||
'phase1_dh_group' => 'required|string|max:50',
|
||||
'phase1_lifetime' => 'required|integer',
|
||||
'phase2_encryption' => 'required|string|max:50',
|
||||
'phase2_hash' => 'required|string|max:50',
|
||||
'phase2_dh_group' => 'nullable|string|max:50',
|
||||
'phase2_lifetime' => 'required|integer',
|
||||
'pfs_enabled' => 'boolean',
|
||||
]);
|
||||
|
||||
|
||||
// If the PSK field is empty, remove it so we don't overwrite the existing key
|
||||
if (empty($validated['preshared_key'])) {
|
||||
unset($validated['preshared_key']);
|
||||
}
|
||||
|
||||
$vpn->update($validated);
|
||||
|
||||
return response()->json(['success' => true, 'message' => 'VPN configuration updated successfully.']);
|
||||
}
|
||||
|
||||
// Export the company's global standard VPN parameters
|
||||
public function downloadStandardTemplate()
|
||||
{
|
||||
$template = "=================================================\n";
|
||||
$template .= " Click Mobile VPN Configuration Parameters\n";
|
||||
$template .= "=================================================\n\n";
|
||||
|
||||
$template .= "! Phase 1 (IKE / ISAKMP)\n";
|
||||
$template .= " Encryption Algorithm: AES-256\n";
|
||||
$template .= " Hash Algorithm: SHA-256\n";
|
||||
$template .= " Authentication Method: Pre-Shared Key (PSK)\n";
|
||||
$template .= " Diffie-Hellman Group: Group 14\n";
|
||||
$template .= " Lifetime: 86400 seconds\n\n";
|
||||
|
||||
$template .= "! Phase 2 (IPsec)\n";
|
||||
$template .= " Protocol: ESP\n";
|
||||
$template .= " Encryption Algorithm: AES-256\n";
|
||||
$template .= " Hash Algorithm: SHA-256\n";
|
||||
$template .= " Perfect Forward Secrecy (PFS): Group 14\n";
|
||||
$template .= " Lifetime: 3600 seconds\n\n";
|
||||
|
||||
$template .= "! Gateway Details\n";
|
||||
$template .= " Our Public Gateway IP: [216.55.137.19]\n";
|
||||
$template .= " Our Encryption Domain: [10.199.0.0/24]\n";
|
||||
|
||||
return response($template)
|
||||
->header('Content-Type', 'text/plain')
|
||||
->header('Content-Disposition', 'attachment; filename="standard_vpn_parameters.txt"');
|
||||
}
|
||||
|
||||
// Fetch data to populate the Partner Export Modal
|
||||
public function generateTemplate($id)
|
||||
{
|
||||
$vpn = VpnConfig::findOrFail($id);
|
||||
|
||||
$template = "=================================================\n";
|
||||
$template .= " VPN Configuration for Partner: {$vpn->partner_name}\n";
|
||||
$template .= "=================================================\n\n";
|
||||
$template .= "! Phase 1 (IKE)\n";
|
||||
$template .= "crypto isakmp policy 10\n";
|
||||
$template .= " encr aes 256\n";
|
||||
$template .= " hash sha256\n";
|
||||
$template .= " authentication pre-share\n";
|
||||
$template .= " group 14\n";
|
||||
$template .= "crypto isakmp key {$vpn->preshared_key} address {$vpn->local_ip}\n\n";
|
||||
$template .= "! Phase 2 (IPsec)\n";
|
||||
$template .= "crypto ipsec transform-set TS-AES256-SHA256 esp-aes 256 esp-sha256-hmac\n";
|
||||
$template .= "crypto map VPNMAP 10 ipsec-isakmp\n";
|
||||
$template .= " set peer {$vpn->local_ip}\n";
|
||||
$template .= " set transform-set TS-AES256-SHA256\n";
|
||||
$template .= " match address 100\n\n";
|
||||
$template .= "! Access Control List (Encryption Domains)\n";
|
||||
$template .= "access-list 100 permit ip {$vpn->encryption_domain_remote} {$vpn->encryption_domain_local}\n";
|
||||
|
||||
return response()->json(['template' => $template, 'partner_name' => $vpn->partner_name]);
|
||||
}
|
||||
|
||||
// Process the edited text and force a file download
|
||||
public function download(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'config_content' => 'required|string',
|
||||
'partner_name' => 'required|string',
|
||||
]);
|
||||
|
||||
$filename = str_replace(' ', '_', strtolower($request->partner_name)) . '_vpn_config.txt';
|
||||
|
||||
return response($request->config_content)
|
||||
->header('Content-Type', 'text/plain')
|
||||
->header('Content-Disposition', "attachment; filename=\"{$filename}\"");
|
||||
}
|
||||
}
|
||||
30
app/Models/ActivityLog.php
Normal file
30
app/Models/ActivityLog.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ActivityLog extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'user_id', 'loggable_type', 'loggable_id', 'action',
|
||||
'old_data', 'new_data', 'ip_address', 'user_agent'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'old_data' => 'array',
|
||||
'new_data' => 'array',
|
||||
];
|
||||
|
||||
// Links back to the User who made the change
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(StaffMember::class);
|
||||
}
|
||||
|
||||
// Links back to the affected Model (Client, Server, VpnConfig, etc.)
|
||||
public function loggable()
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,10 @@
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
use App\Traits\LogsActivity;
|
||||
class Client extends Model
|
||||
{
|
||||
use LogsActivity;
|
||||
protected $guarded = [
|
||||
'id'
|
||||
];
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Traits\LogsActivity;
|
||||
|
||||
class ClientFile extends Model
|
||||
{
|
||||
use LogsActivity;
|
||||
protected $guarded = array('id');
|
||||
|
||||
public function client_info(){
|
||||
|
||||
@@ -5,9 +5,11 @@ namespace App\Models;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Spatie\Activitylog\Traits\LogsActivity;
|
||||
use Spatie\Activitylog\LogOptions;
|
||||
use App\Traits\LogsActivity;
|
||||
|
||||
class ClientNote extends Model
|
||||
{
|
||||
use LogsActivity;
|
||||
protected $guarded = array('id');
|
||||
protected static $logUnguarded = true;
|
||||
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Traits\LogsActivity;
|
||||
|
||||
class DailyReport extends Model
|
||||
{
|
||||
use LogsActivity;
|
||||
protected $fillable = ['user_id', 'report_date', 'content'];
|
||||
protected $casts = [
|
||||
'report_date' => 'date',
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Traits\LogsActivity;
|
||||
|
||||
class LeaveManagement extends Model
|
||||
{
|
||||
use LogsActivity;
|
||||
protected $guarded = array('id');
|
||||
public $table = "leave_management";
|
||||
|
||||
|
||||
@@ -15,10 +15,4 @@ class LeaveRequest extends Model
|
||||
{
|
||||
return $this->belongsTo(StaffMember::class, 'staff_member_id');
|
||||
}
|
||||
|
||||
// In StaffMember.php
|
||||
public function leaveRequests()
|
||||
{
|
||||
return $this->hasMany(LeaveRequest::class, 'staff_member_id');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,11 @@ namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use App\Traits\LogsActivity;
|
||||
|
||||
class NetworkOperator extends Model
|
||||
{
|
||||
use LogsActivity;
|
||||
protected $guarded = array('id');
|
||||
public $table = "network_operators";
|
||||
|
||||
@@ -15,6 +17,8 @@ class NetworkOperator extends Model
|
||||
'support_emails' => 'array',
|
||||
'finance_emails' => 'array',
|
||||
'bind_specifics' => 'array',
|
||||
'contract_auto_renew' => 'boolean',
|
||||
'next_renewal_date' => 'date',
|
||||
];
|
||||
public function account_manager_info(){
|
||||
return $this->hasOne('App\Models\StaffMember', 'id', 'account_manager_id');
|
||||
@@ -23,6 +27,10 @@ class NetworkOperator extends Model
|
||||
{
|
||||
return $this->hasMany(NetworkOperatorConnection::class, 'network_operator_id');
|
||||
}
|
||||
public function documents()
|
||||
{
|
||||
return $this->hasMany(NetworkOperatorDocument::class);
|
||||
}
|
||||
/*
|
||||
public function setBuyingRateAttribute($value)
|
||||
{
|
||||
|
||||
17
app/Models/NetworkOperatorDocument.php
Normal file
17
app/Models/NetworkOperatorDocument.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Traits\LogsActivity;
|
||||
|
||||
class NetworkOperatorDocument extends Model
|
||||
{
|
||||
use LogsActivity;
|
||||
protected $fillable = ['network_operator_id', 'name', 'file_path'];
|
||||
|
||||
public function networkOperator()
|
||||
{
|
||||
return $this->belongsTo(NetworkOperator::class);
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,11 @@
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Traits\LogsActivity;
|
||||
|
||||
class OfficeLocation extends Model
|
||||
{
|
||||
use LogsActivity;
|
||||
protected $guarded = array('id');
|
||||
protected $table = 'office_locations';
|
||||
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Traits\LogsActivity;
|
||||
|
||||
class OfficeLocationDocument extends Model
|
||||
{
|
||||
use LogsActivity;
|
||||
protected $table = 'office_location_documents';
|
||||
protected $guarded = [];
|
||||
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Spatie\Activitylog\Traits\LogsActivity;
|
||||
use Spatie\Activitylog\LogOptions;
|
||||
// use Spatie\Activitylog\Traits\LogsActivity;
|
||||
// use Spatie\Activitylog\LogOptions;
|
||||
use App\Traits\LogsActivity;
|
||||
|
||||
class SenderId extends Model{
|
||||
// use LogsActivity;
|
||||
|
||||
|
||||
use LogsActivity;
|
||||
protected $guarded = array('id');
|
||||
public $table = "sender_ids";
|
||||
protected static $logName = 'senderid_log';
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Traits\LogsActivity;
|
||||
|
||||
class Senderidold extends Model{
|
||||
|
||||
|
||||
use LogsActivity;
|
||||
protected $guarded = array('id');
|
||||
public $table = "sender_ids_copy";
|
||||
|
||||
|
||||
22
app/Models/Server.php
Normal file
22
app/Models/Server.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Traits\LogsActivity;
|
||||
|
||||
class Server extends Model
|
||||
{
|
||||
use LogsActivity;
|
||||
protected $fillable = [
|
||||
'name', 'ip_address', 'os_version', 'username', 'password', 'services', 'status'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'password' => 'encrypted', // Automatically encrypts/decrypts
|
||||
'services' => 'array', // Casts JSON to PHP array
|
||||
];
|
||||
|
||||
// Hide password from standard array/JSON serialization for safety
|
||||
protected $hidden = ['password'];
|
||||
}
|
||||
@@ -3,9 +3,12 @@
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Traits\LogsActivity;
|
||||
|
||||
|
||||
class ShortCode extends Model
|
||||
{
|
||||
use LogsActivity;
|
||||
protected $guarded = array('id');
|
||||
public $table = "client_short_codes";
|
||||
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Traits\LogsActivity;
|
||||
|
||||
class StaffDependent extends Model
|
||||
{
|
||||
use LogsActivity;
|
||||
protected $table = 'staff_dependents';
|
||||
protected $guarded = [];
|
||||
|
||||
|
||||
@@ -5,10 +5,11 @@ namespace App\Models;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use App\Traits\LogsActivity;
|
||||
|
||||
class StaffMember extends Authenticatable
|
||||
{
|
||||
use HasFactory, Notifiable;
|
||||
use HasFactory, Notifiable, LogsActivity;
|
||||
|
||||
// 1. Explicitly define the custom table name
|
||||
protected $table = 'staff_members';
|
||||
@@ -34,6 +35,7 @@ class StaffMember extends Authenticatable
|
||||
public function department(){
|
||||
return $this->hasOne('App\Models\Department', 'id', 'department_id');
|
||||
}
|
||||
|
||||
// 4. Cast dates correctly
|
||||
protected $casts = [
|
||||
// 'dob' => 'date',
|
||||
|
||||
@@ -3,10 +3,23 @@
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Traits\LogsActivity;
|
||||
|
||||
class VpnConfig extends Model
|
||||
{
|
||||
protected $guarded = ['id'];
|
||||
//protected $guarded = ['id'];
|
||||
use LogsActivity;
|
||||
protected $fillable = [
|
||||
'partner_name', 'peer_ip', 'local_ip',
|
||||
'local_device', 'peer_device',
|
||||
'encryption_domain_local', 'encryption_domain_remote',
|
||||
'preshared_key', 'status',
|
||||
'ike_version', 'phase1_encryption', 'phase1_hash', 'phase1_dh_group', 'phase1_lifetime',
|
||||
'phase2_encryption', 'phase2_hash', 'phase2_dh_group', 'phase2_lifetime', 'pfs_enabled'
|
||||
];
|
||||
protected $casts = [
|
||||
'preshared_key' => 'encrypted',
|
||||
];
|
||||
|
||||
public function client_info(){
|
||||
return $this->hasOne('App\Models\Client', 'id', 'client_id');
|
||||
@@ -15,6 +28,6 @@ class VpnConfig extends Model
|
||||
return $this->hasOne('App\Models\NetworkOps', 'id', 'mno_id');
|
||||
}
|
||||
public function userInfo(){
|
||||
return $this->hasOne('App\Models\SystemUser', 'id', 'user_id');
|
||||
return $this->hasOne('App\Models\StaffMember', 'id', 'user_id');
|
||||
}
|
||||
}
|
||||
|
||||
65
app/Traits/LogsActivity.php
Normal file
65
app/Traits/LogsActivity.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php namespace App\Traits;
|
||||
|
||||
use App\Models\ActivityLog;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
trait LogsActivity
|
||||
{
|
||||
// Laravel automatically calls boot[TraitName] when booting the model
|
||||
protected static function bootLogsActivity()
|
||||
{
|
||||
static::created(function ($model) {
|
||||
$model->recordActivity('created', null, $model->getAttributes());
|
||||
});
|
||||
|
||||
static::updated(function ($model) {
|
||||
// Get only the fields that actually changed
|
||||
$newAttributes = $model->getChanges();
|
||||
|
||||
// Ignore the 'updated_at' timestamp noise
|
||||
unset($newAttributes['updated_at']);
|
||||
|
||||
if (!empty($newAttributes)) {
|
||||
$oldAttributes = array_intersect_key($model->getOriginal(), $newAttributes);
|
||||
$model->recordActivity('updated', $oldAttributes, $newAttributes);
|
||||
}
|
||||
});
|
||||
|
||||
static::deleted(function ($model) {
|
||||
$model->recordActivity('deleted', $model->getAttributes(), null);
|
||||
});
|
||||
}
|
||||
|
||||
protected function recordActivity($action, $oldData = null, $newData = null)
|
||||
{
|
||||
// Define sensitive fields to NEVER log
|
||||
$hiddenFields = ['password', 'preshared_key', 'remember_token'];
|
||||
|
||||
// Allow models to define custom fields to ignore via a protected $ignoreLogs array
|
||||
if (property_exists($this, 'ignoreLogs')) {
|
||||
$hiddenFields = array_merge($hiddenFields, $this->ignoreLogs);
|
||||
}
|
||||
|
||||
$cleanOld = $oldData ? collect($oldData)->except($hiddenFields)->toArray() : null;
|
||||
$cleanNew = $newData ? collect($newData)->except($hiddenFields)->toArray() : null;
|
||||
|
||||
ActivityLog::create([
|
||||
'user_id' => Auth::id(),
|
||||
'loggable_type' => get_class($this),
|
||||
'loggable_id' => $this->id,
|
||||
'action' => $action,
|
||||
'old_data' => empty($cleanOld) ? null : $cleanOld,
|
||||
'new_data' => empty($cleanNew) ? null : $cleanNew,
|
||||
'ip_address' => request()->ip(),
|
||||
'user_agent' => request()->userAgent()
|
||||
]);
|
||||
}
|
||||
|
||||
// Helper to fetch logs for a specific model instance
|
||||
public function activityLogs()
|
||||
{
|
||||
return $this->morphMany(ActivityLog::class, 'loggable')->latest();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
Reference in New Issue
Block a user