chore: initial commit

This commit is contained in:
Oussama Douhou
2025-10-29 11:09:43 +01:00
commit 7ec667258a
235 changed files with 62997 additions and 0 deletions

104
api/views/activate.php Normal file
View File

@@ -0,0 +1,104 @@
<?php defined('BASEPATH') || exit('No direct script access allowed'); ?>
<?php init_head(); ?>
<div id="wrapper">
<div class="content">
<div class="row">
<div class="col-md-6">
<div class="panel_s">
<div class="panel-body">
<h4>Module Activation 🔑</h4>
<hr class="hr-panel-heading">
<p>👉 Enter your license purchase key below (<a target="_blank" href="https://help.market.envato.com/hc/en-us/articles/202822600-Where-Is-My-Purchase-Code-">Where do I get this from?</a>)</p>
<br>
<?php echo form_open($submit_url, ['autocomplete' => 'off', 'id' => 'verify-form']); ?>
<?php echo form_hidden('original_url', $original_url); ?>
<?php echo form_hidden('module_name', $module_name); ?>
<?php echo render_input('purchase_key', 'purchase_key', '', 'text', ['required' => true]); ?>
<div class="checkbox">
<input type="checkbox" id="confirmation" name="confirmation" required value="">
<label for="confirmation">I confirm that I adhere to the <a href="https://codecanyon.net/licenses/standard" target="_blank">Envato Licensing Terms</a></label>
</div>
<div class="row mbot20">
<div class="col-md-12">
<br>
<button id="submit" type="submit" class="btn btn-primary">Click to activate ✔️</button>
</div>
</div><br>
<?php echo form_close(); ?>
</div>
<div class="panel-footer">
{} <?php echo 'Version ' . $this->app_modules->get($module_name)['headers']['version'] ?? ''; ?>
</div>
</div>
</div>
<div class="col-md-6">
<div class="panel_s">
<div class="panel-body">
<center>
<i class="fa fa-gavel fa-fw fa-lg tw-mr-0.5 group-hover:tw-text-neutral-800 tw-text-neutral-300"></i><h4>License Compliance & Terms</h4>
<ul class="list-unstyled">
<li><i class="fa fa-check-circle"></i> Your license is valid for one instance only.</li>
<li><i class="fa fa-check-circle"></i> A Regular License allows you to create one end product for yourself/a client.</li>
<li><i class="fa fa-exclamation-circle"></i> Violations of these terms may result in license termination.</li>
</ul>
<br>
<i class="fa fa-thumbs-down fa-fw fa-lg tw-mr-0.5 group-hover:tw-text-neutral-800 tw-text-neutral-300"></i><h4>Prohibited Usage</h4>
<ul class="list-unstyled">
<li><i class="fa fa-ban"></i> Resell product/end product to multiple clients without an Extended License.</li>
<li><i class="fa fa-ban"></i> Redistribute the product as-is or with minor modifications.</li>
<li><i class="fa fa-ban"></i> Use the product in third party applications as a bundled product.</li>
<li><i class="fa fa-ban"></i> Extract code components from the product for separate use.</li>
<li><i class="fa fa-ban"></i> Edit the copyrights of the code (name, author etc).</li>
</ul>
</center>
</div>
<div class="panel-footer">
<center>📋 <a style="color:#000" href="https://codecanyon.net/licenses/terms/regular">Envato License Aggrement »</center>
</div>
</div>
</div>
</div>
</div>
</div>
<?php init_tail(); ?>
<script type="text/javascript">
"use strict";
appValidateForm($('#verify-form'), {
purchase_key: 'required',
confirmation: 'required'
}, manage_verify_form, {
confirmation: {
required: "You didnt accept the terms - Please reload the page to activate your license."
}
});
function manage_verify_form(form) {
// Get and trim the value of the purchase_key input field
var purchaseKey = $('#purchase_key').val().trim();
// Log the value to check if trimming is happening correctly
console.log("Trimmed Purchase Key: '" + purchaseKey + "'");
// Set the trimmed value back to the input field
$('#purchase_key').val(purchaseKey);
// Disable the submit button and show loading icon
$("#submit").prop('disabled', true).prepend('<i class="fa fa-spinner fa-pulse"></i> ');
// Send the form data using AJAX
$.post(form.action, $(form).serialize()).done(function(response) {
var response = $.parseJSON(response);
if (!response.status) {
alert_float("danger", response.message);
}
if (response.status) {
alert_float("success", "Activating your license..");
window.location.href = response.original_url;
}
// Enable the submit button and remove the loading icon
$("#submit").prop('disabled', false).find('i').remove();
});
}
</script>

View File

@@ -0,0 +1,94 @@
<?php defined('BASEPATH') or exit('No direct script access allowed'); ?>
<?php init_head(); ?>
<link href="<?php echo base_url('modules/api/assets/main.css'); ?>" rel="stylesheet" type="text/css" />
<div id="wrapper">
<div class="content">
<div class="row">
<div class="col-md-12">
<div class="panel_s">
<div class="panel-body">
<div class="_buttons">
<a href="<?php echo admin_url('api/create_user/') ?>" class="btn btn-info pull-left display-block"><?php echo _l('new_user_api'); ?></a>
</div>
<div class="clearfix"></div>
<hr class="hr-panel-heading" />
<div class="clearfix"></div>
<table class="apitable table dt-table">
<thead>
<th><?php echo _l('id'); ?></th>
<th><?php echo _l('user_api'); ?></th>
<th><?php echo _l('name_api'); ?></th>
<th><?php echo _l('token_api'); ?></th>
<th><?php echo _l('request_limit'); ?></th>
<th><?php echo _l('time_window'); ?></th>
<th><?php echo _l('quota_active'); ?></th>
<th><?php echo _l('expiration_date'); ?></th>
<th><?php echo _l('options'); ?></th>
</thead>
<tbody>
<?php foreach($user_api as $user) { ?>
<tr>
<td><?php echo addslashes($user['id']); ?></td>
<td><?php echo addslashes($user['user']); ?></td>
<td><?php echo addslashes($user['name']); ?></td>
<td><code onclick="copyToken('<?php echo $user['token'] ?>')"><?php echo substr($user['token'], 0, 20) . '...'; ?></code></td>
<td><?php echo addslashes($user['request_limit'] ?? 1000); ?></td>
<td><?php echo format_time_window($user['time_window'] ?? 3600); ?></td>
<td>
<span class="label label-<?php echo ($user['quota_active'] ?? 1) ? 'success' : 'danger'; ?>">
<?php echo ($user['quota_active'] ?? 1) ? _l('active') : _l('inactive'); ?>
</span>
</td>
<td><?php echo addslashes($user['expiration_date'] ?? ""); ?></td>
<td>
<a href="<?php echo admin_url('api/edit_user/' . $user['id']) ?>" class="btn btn-default btn-icon" title="<?php echo _l('edit_user'); ?>"><i class="fa fa-pencil"></i></a>
<a href="<?php echo admin_url('api/user_stats/' . $user['id']) ?>" class="btn btn-success btn-icon" title="<?php echo _l('user_statistics'); ?>"><i class="fa fa-bar-chart"></i></a>
<a href="<?php echo admin_url('api/delete_user/' . addslashes($user['id'])); ?>" class="btn btn-danger btn-icon _delete" title="<?php echo _l('delete_user'); ?>"><i class="fa fa-remove"></i></a>
</td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal fade" id="user_api" tabindex="-1" role="dialog">
<div class="modal-dialog">
<?php echo form_open(admin_url('api/user')); ?>
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">
<span class="edit-title"><?php echo _l('edit_user_api'); ?></span>
<span class="add-title"><?php echo _l('new_user_api'); ?></span>
</h4>
</div>
<div class="modal-body">
<div class="row">
<div class="col-md-12">
<div id="additional"></div>
<?php echo render_input('user','user_api'); ?>
<?php echo render_input('name','name_api'); ?>
<?php echo render_datetime_input('expiration_date','expiration_date'); ?>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal"><?php echo _l('close'); ?></button>
<button type="submit" class="btn btn-info"><?php echo _l('submit'); ?></button>
</div>
</div><!-- /.modal-content -->
<?php echo form_close(); ?>
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<?php init_tail(); ?>
<script src="<?php echo base_url('modules/api/assets/main.js'); ?>"></script>

275
api/views/api_reporting.php Normal file
View File

@@ -0,0 +1,275 @@
<?php defined('BASEPATH') or exit('No direct script access allowed'); ?>
<?php init_head(); ?>
<link href="<?php echo base_url('modules/api/assets/main.css'); ?>" rel="stylesheet" type="text/css" />
<script src="https://code.highcharts.com/highcharts.js"></script>
<div id="wrapper">
<div class="content">
<div class="row">
<div class="col-md-12">
<div class="panel_s">
<div class="panel-body">
<div class="row">
<div class="col-md-12">
<h4 class="no-margin"><?php echo _l('api_reporting'); ?></h4>
<hr class="hr-panel-heading" />
</div>
</div>
<!-- Filters -->
<div class="row">
<div class="col-md-12">
<div class="panel panel-info">
<div class="panel-heading">
<h4 class="panel-title"><?php echo _l('filters'); ?></h4>
</div>
<div class="panel-body">
<?php echo form_open(admin_url('api/reporting'), 'method="get"', ['id' => 'reporting-filters']); ?>
<div class="row">
<div class="col-md-3">
<div class="form-group">
<label for="api_key"><?php echo _l('api_key'); ?></label>
<select name="api_key" id="api_key" class="form-control">
<option value=""><?php echo _l('all_api_keys'); ?></option>
<?php foreach ($api_keys as $key) { ?>
<option value="<?php echo $key['api_key']; ?>" <?php echo ($api_key == $key['api_key']) ? 'selected' : ''; ?>>
<?php echo $key['api_key']; ?>
</option>
<?php } ?>
</select>
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label for="start_date"><?php echo _l('start_date'); ?></label>
<input type="date" name="start_date" id="start_date" class="form-control" value="<?php echo $start_date; ?>">
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label for="end_date"><?php echo _l('end_date'); ?></label>
<input type="date" name="end_date" id="end_date" class="form-control" value="<?php echo $end_date; ?>">
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label>&nbsp;</label>
<div>
<button type="submit" class="btn btn-info"><?php echo _l('apply_filters'); ?></button>
<a href="<?php echo admin_url('api/reporting/export?' . http_build_query($_GET)); ?>" class="btn btn-success"><?php echo _l('export'); ?></a>
</div>
</div>
</div>
</div>
<?php echo form_close(); ?>
</div>
</div>
</div>
</div>
<!-- Summary Cards -->
<div class="row">
<div class="col-md-3">
<div class="panel panel-primary">
<div class="panel-heading">
<h4 class="panel-title"><?php echo _l('total_requests'); ?></h4>
</div>
<div class="panel-body text-center">
<h2><?php echo number_format($usage_stats->total_requests ?? 0); ?></h2>
</div>
</div>
</div>
<div class="col-md-3">
<div class="panel panel-success">
<div class="panel-heading">
<h4 class="panel-title"><?php echo _l('success_rate'); ?></h4>
</div>
<div class="panel-body text-center">
<h2><?php echo ($usage_stats->total_requests ?? 0) > 0 ? round((($usage_stats->success_requests ?? 0) / ($usage_stats->total_requests ?? 1)) * 100, 2) : 0; ?>%</h2>
</div>
</div>
</div>
<div class="col-md-3">
<div class="panel panel-warning">
<div class="panel-heading">
<h4 class="panel-title"><?php echo _l('avg_response_time'); ?></h4>
</div>
<div class="panel-body text-center">
<h2><?php echo round($usage_stats->avg_response_time ?? 0, 4); ?>s</h2>
</div>
</div>
</div>
<div class="col-md-3">
<div class="panel panel-danger">
<div class="panel-heading">
<h4 class="panel-title"><?php echo _l('error_requests'); ?></h4>
</div>
<div class="panel-body text-center">
<h2><?php echo number_format($usage_stats->error_requests ?? 0); ?></h2>
</div>
</div>
</div>
</div>
<!-- Charts -->
<div class="row">
<div class="col-md-8">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title"><?php echo _l('request_timeline'); ?></h4>
</div>
<div class="panel-body">
<div id="request-timeline-chart" style="height: 400px;"></div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title"><?php echo _l('response_codes'); ?></h4>
</div>
<div class="panel-body">
<div id="response-codes-chart" style="height: 400px;"></div>
</div>
</div>
</div>
</div>
<!-- Endpoint Statistics -->
<div class="row">
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title"><?php echo _l('endpoint_statistics'); ?></h4>
</div>
<div class="panel-body">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th><?php echo _l('endpoint'); ?></th>
<th><?php echo _l('request_count'); ?></th>
<th><?php echo _l('avg_response_time'); ?></th>
<th><?php echo _l('success_count'); ?></th>
<th><?php echo _l('error_count'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($endpoint_stats as $stat) { ?>
<tr>
<td><?php echo $stat->endpoint; ?></td>
<td><?php echo number_format($stat->request_count ?? 0); ?></td>
<td><?php echo round($stat->avg_response_time ?? 0, 4); ?>s</td>
<td><?php echo number_format($stat->success_count ?? 0); ?></td>
<td><?php echo number_format($stat->error_count ?? 0); ?></td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- API Key Summary -->
<div class="row">
<div class="col-md-12">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title"><?php echo _l('api_key_summary'); ?></h4>
</div>
<div class="panel-body">
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th><?php echo _l('api_key'); ?></th>
<th><?php echo _l('total_requests'); ?></th>
<th><?php echo _l('avg_response_time'); ?></th>
<th><?php echo _l('success_requests'); ?></th>
<th><?php echo _l('error_requests'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($api_key_summary as $summary) { ?>
<tr>
<td><?php echo $summary->api_key; ?></td>
<td><?php echo number_format($summary->total_requests ?? 0); ?></td>
<td><?php echo round($summary->avg_response_time ?? 0, 4); ?>s</td>
<td><?php echo number_format($summary->success_requests ?? 0); ?></td>
<td><?php echo number_format($summary->error_requests ?? 0); ?></td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<?php init_tail(); ?>
<script>
$(document).ready(function() {
// Request Timeline Chart
var timelineData = <?php echo json_encode($hourly_usage); ?>;
var timelineChart = Highcharts.chart('request-timeline-chart', {
title: {
text: '<?php echo _l('request_timeline'); ?>'
},
xAxis: {
type: 'datetime',
title: {
text: '<?php echo _l('time'); ?>'
}
},
yAxis: {
title: {
text: '<?php echo _l('requests'); ?>'
}
},
series: [{
name: '<?php echo _l('requests'); ?>',
data: timelineData.map(function(item) {
return [new Date(item.hour).getTime(), parseInt(item.request_count)];
})
}],
tooltip: {
pointFormat: '{series.name}: <b>{point.y}</b>'
}
});
// Response Codes Chart
var responseCodesData = <?php echo json_encode($response_codes); ?>;
var responseCodesChart = Highcharts.chart('response-codes-chart', {
chart: {
type: 'pie'
},
title: {
text: '<?php echo _l('response_codes'); ?>'
},
series: [{
name: '<?php echo _l('requests'); ?>',
data: responseCodesData.map(function(item) {
return [item.response_code.toString(), parseInt(item.count)];
})
}],
tooltip: {
pointFormat: '{series.name}: <b>{point.y}</b>'
}
});
});
</script>
<script src="<?php echo base_url('modules/api/assets/main.js'); ?>"></script>

View File

@@ -0,0 +1,83 @@
<?php defined('BASEPATH') or exit('No direct script access allowed'); ?>
<?php init_head(); ?>
<link href="<?php echo base_url('modules/api/assets/main.css'); ?>" rel="stylesheet" type="text/css" />
<div id="wrapper">
<div class="content">
<?php echo form_open('admin/api/user/'); ?>
<div class="row">
<div class="col-md-4">
<?php echo render_input('user', 'user_api'); ?>
</div>
<div class="col-md-4">
<?php echo render_input('name', 'name_api'); ?>
</div>
<div class="col-md-4">
<?php echo render_datetime_input('expiration_date', 'expiration_date'); ?>
</div>
</div>
<div class="row">
<div class="col-md-12">
<h4 class="no-margin"><?php echo _l('quota_settings'); ?></h4>
<hr class="hr-panel-heading" />
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label for="request_limit" class="control-label"><?php echo _l('request_limit'); ?> <span class="text-danger">*</span></label>
<input type="number" class="form-control" name="request_limit" id="request_limit" value="1000" min="1" required>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="time_window" class="control-label"><?php echo _l('time_window'); ?> <span class="text-danger">*</span></label>
<select class="form-control" name="time_window" id="time_window" required>
<option value="3600">1 Hour</option>
<option value="86400" selected>24 Hours</option>
<option value="604800">7 Days</option>
<option value="2592000">30 Days</option>
</select>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="burst_limit" class="control-label"><?php echo _l('burst_limit'); ?> <span class="text-danger">*</span></label>
<input type="number" class="form-control" name="burst_limit" id="burst_limit" value="100" min="1" required>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<div class="checkbox">
<input type="checkbox" name="quota_active" id="quota_active" value="1" checked>
<label><?php echo _l('quota_active'); ?></label>
</div>
</div>
</div>
</div>
<?php $this->load->view('permissions'); ?>
<div class="row">
<div class="col-md-12">
<button type="submit" class="btn btn-primary pull-right permission-save-btn" id="permission-form-submit">
<?php echo _l('submit'); ?>
</button>
</div>
</div>
<?php echo form_close(); ?>
</div>
</div>
<?php init_tail(); ?>
<script src="<?php echo base_url('modules/api/assets/main.js'); ?>"></script>

View File

@@ -0,0 +1,92 @@
<?php defined('BASEPATH') or exit('No direct script access allowed'); ?>
<?php init_head(); ?>
<link href="<?php echo base_url('modules/api/assets/main.css'); ?>" rel="stylesheet" type="text/css" />
<div id="wrapper">
<div class="content">
<?php echo form_open('admin/api/user/'); ?>
<input type="hidden" name="id" value="<?php echo $user_api['id'] ?? ''?>" />
<div class="row">
<div class="col-md-4">
<?php echo render_input('user', 'user_api', $user_api['user'] ?? ''); ?>
</div>
<div class="col-md-4">
<?php echo render_input('name', 'name_api', $user_api['name'] ?? ''); ?>
</div>
<div class="col-md-4">
<?php echo render_datetime_input('expiration_date', 'expiration_date', $user_api['expiration_date'] ?? ''); ?>
</div>
</div>
<div class="row">
<div class="col-md-12">
<?php echo render_input('token', 'token_api', $user_api['token'] ?? '', 'text', ['readonly' => true]); ?>
</div>
</div>
<div class="row">
<div class="col-md-12">
<h4 class="no-margin"><?php echo _l('quota_settings'); ?></h4>
<hr class="hr-panel-heading" />
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-group">
<label for="request_limit" class="control-label"><?php echo _l('request_limit'); ?> <span class="text-danger">*</span></label>
<input type="number" class="form-control" name="request_limit" id="request_limit"
value="<?php echo $user_api['request_limit'] ?? 1000; ?>" min="1" required>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="time_window" class="control-label"><?php echo _l('time_window'); ?> <span class="text-danger">*</span></label>
<select class="form-control" name="time_window" id="time_window" required>
<option value="3600" <?php echo ($user_api['time_window'] ?? 3600) == 3600 ? 'selected' : ''; ?>>1 Hour</option>
<option value="86400" <?php echo ($user_api['time_window'] ?? 3600) == 86400 ? 'selected' : ''; ?>>24 Hours</option>
<option value="604800" <?php echo ($user_api['time_window'] ?? 3600) == 604800 ? 'selected' : ''; ?>>7 Days</option>
<option value="2592000" <?php echo ($user_api['time_window'] ?? 3600) == 2592000 ? 'selected' : ''; ?>>30 Days</option>
</select>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label for="burst_limit" class="control-label"><?php echo _l('burst_limit'); ?> <span class="text-danger">*</span></label>
<input type="number" class="form-control" name="burst_limit" id="burst_limit"
value="<?php echo $user_api['burst_limit'] ?? 100; ?>" min="1" required>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<div class="checkbox">
<input type="checkbox" name="quota_active" id="quota_active" value="1"
<?php echo ($user_api['quota_active'] ?? 1) ? 'checked' : ''; ?>/>
<label><?php echo _l('quota_active'); ?></label>
</div>
</div>
</div>
</div>
<?php $this->load->view('permissions'); ?>
<div class="row">
<div class="col-md-12">
<button type="submit" class="btn btn-primary pull-right permission-save-btn" id="permission-form-submit">
<?php echo _l('submit'); ?>
</button>
</div>
</div>
<?php echo form_close(); ?>
</div>
</div>
<?php init_tail(); ?>
<script src="<?php echo base_url('modules/api/assets/main.js'); ?>"></script>

104
api/views/permissions.php Normal file
View File

@@ -0,0 +1,104 @@
<?php $api_permissions_count = count(get_available_api_permissions()); ?>
<div class="row">
<div class="col-md-12">
<?php echo _l('permissions'); ?>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="panel_s">
<div class="panel-body">
<div class="table-responsive">
<table class="table table-bordered roles no-margin">
<thead>
<tr>
<th>Feature</th>
<th>Capabilities</th>
</tr>
</thead>
<tbody>
<?php
$api_permission_index = 0;
foreach (get_available_api_permissions() as $feature => $permission) {
$api_permission_index += 1;
if ($api_permission_index >= floor($api_permissions_count / 2)) continue;
?>
<tr data-name="<?php echo $feature; ?>">
<td>
<b><?php echo $permission['name']; ?></b>
</td>
<td>
<?php
foreach ($permission['capabilities'] as $capability => $name) {
$checked = '';
if (isset($user_api) && api_can($user_api['id'] ?? '', $feature, $capability)) {
$checked = ' checked ';
}
?>
<div class="checkbox" style="padding-left: 20px">
<input type="checkbox" <?php echo $checked; ?> class="capability" id="<?php echo $feature . '_' . $capability; ?>" name="permissions[<?php echo $feature; ?>][]" value="<?php echo $capability; ?>">
<label for="<?php echo $feature . '_' . $capability; ?>"> <?php echo $name; ?></label>
</div>
<?php
}
?>
</td>
</tr>
<?php }
?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="panel_s">
<div class="panel-body">
<div class="table-responsive">
<table class="table table-bordered roles no-margin">
<thead>
<tr>
<th>Feature</th>
<th>Capabilities</th>
</tr>
</thead>
<tbody>
<?php
$api_permission_index = 0;
foreach (get_available_api_permissions() as $feature => $permission) {
$api_permission_index += 1;
if ($api_permission_index < floor($api_permissions_count / 2)) continue;
?>
<tr data-name="<?php echo $feature; ?>">
<td>
<b><?php echo $permission['name']; ?></b>
</td>
<td>
<?php
foreach ($permission['capabilities'] as $capability => $name) {
$checked = '';
if (isset($user_api) && api_can($user_api['id'] ?? '', $feature, $capability)) {
$checked = ' checked ';
}
?>
<div class="checkbox" style="padding-left: 20px">
<input type="checkbox" <?php echo $checked; ?> class="capability" id="<?php echo $feature . '_' . $capability; ?>" name="permissions[<?php echo $feature; ?>][]" value="<?php echo $capability; ?>">
<label for="<?php echo $feature . '_' . $capability; ?>"> <?php echo $name; ?></label>
</div>
<?php
}
?>
</td>
</tr>
<?php }
?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>

66
api/views/playground.php Normal file
View File

@@ -0,0 +1,66 @@
<!DOCTYPE html>
<html>
<head>
<title><?php echo $title; ?></title>
<link rel="stylesheet" type="text/css" href="<?php echo base_url('/modules/api/assets/swagger/swagger-ui.css') ?>">
<script src="<?php echo base_url('/modules/api/assets/swagger/swagger-ui-bundle.js') ?>"></script>
<script src="<?php echo base_url('/modules/api/assets/swagger/swagger-ui-standalone-preset.js') ?>"></script>
</head>
<body>
<div id="swagger-ui"></div>
<style>
[data-param-name="check_api"] {
display: none;
}
.schemes-server-container {
opacity: 0;
}
.swagger-ui > div > .wrapper:nth-child(5) {
display: none;
}
</style>
<script>
window.onload = function() {
const ui = SwaggerUIBundle({
url: "<?php echo site_url('/api/playground-json'); ?>",
dom_id: '#swagger-ui',
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIBundle.SwaggerUIStandalonePreset
],
plugins: [ ],
requestInterceptor: (req) => {
req.headers['X-Playground'] = 'enabled';
return req;
}
});
}
function checkTryOutBtn() {
if (document.querySelectorAll('.try-out__btn').length) {
document.querySelectorAll('.try-out__btn').forEach(btyOutBtnEl => {
btyOutBtnEl.removeEventListener('click', function() {});
btyOutBtnEl.addEventListener('click', function() {
setTimeout(function() {
document.querySelectorAll('input').forEach(decimalEl => {
if (decimalEl.closest('.parameters').querySelector('.prop-format') && decimalEl.closest('.parameters').querySelector('.prop-format').innerText === "($decimal)") {
decimalEl.value = parseFloat(decimalEl.value).toFixed(2);
decimalEl.setAttribute("value", parseFloat(decimalEl.value).toFixed(2));
decimalEl.removeEventListener('change', function() {});
decimalEl.addEventListener('change', function() {
decimalEl.value = parseFloat(decimalEl.value).toFixed(2);
decimalEl.setAttribute("value", parseFloat(decimalEl.value).toFixed(2));
});
}
});
}, 1000);
});
});
}
setTimeout(function() {
checkTryOutBtn();
}, 1000);
}
checkTryOutBtn();
</script>
</body>
</html>

View File

@@ -0,0 +1,307 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo $title; ?></title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/themes/prism.min.css" rel="stylesheet">
<style>
body {
background-color: #f8f9fa;
}
.documentation-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.header-section {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 40px 0;
margin-bottom: 30px;
border-radius: 10px;
}
.content-section {
background: white;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.section-header {
background: #f8f9fa;
padding: 15px 20px;
border-bottom: 1px solid #dee2e6;
border-radius: 10px 10px 0 0;
font-weight: 600;
}
.section-body {
padding: 20px;
}
.endpoint-item {
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 5px;
padding: 15px;
margin-bottom: 15px;
}
.method-badge {
display: inline-block;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: bold;
text-transform: uppercase;
}
.method-get { background: #28a745; color: white; }
.method-post { background: #007bff; color: white; }
.method-put { background: #ffc107; color: black; }
.method-delete { background: #dc3545; color: white; }
.code-block {
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 5px;
padding: 15px;
font-family: 'Courier New', monospace;
font-size: 14px;
overflow-x: auto;
}
.nav-link {
color: #495057;
text-decoration: none;
padding: 8px 16px;
border-radius: 5px;
transition: all 0.3s ease;
}
.nav-link:hover {
background-color: #e9ecef;
color: #212529;
}
.nav-link.active {
background-color: #007bff;
color: white;
}
</style>
</head>
<body>
<div class="documentation-container">
<!-- Header Section -->
<div class="header-section text-center">
<h1><i class="fas fa-book"></i> Perfex CRM API Documentation</h1>
<p class="lead">Complete guide to using the Perfex CRM REST API</p>
</div>
<!-- Navigation -->
<div class="content-section">
<div class="section-header">
<h4><i class="fas fa-list"></i> Quick Navigation</h4>
</div>
<div class="section-body">
<div class="row">
<div class="col-md-3">
<a href="#authentication" class="nav-link">Authentication</a>
</div>
<div class="col-md-3">
<a href="#endpoints" class="nav-link">API Endpoints</a>
</div>
<div class="col-md-3">
<a href="#examples" class="nav-link">Examples</a>
</div>
<div class="col-md-3">
<a href="#playground" class="nav-link">API Playground</a>
</div>
</div>
</div>
</div>
<!-- Authentication Section -->
<div class="content-section" id="authentication">
<div class="section-header">
<h4><i class="fas fa-key"></i> Authentication</h4>
</div>
<div class="section-body">
<p>The Perfex CRM API uses JWT (JSON Web Token) authentication. You need to include your API key in the Authorization header of each request.</p>
<h5>Getting Your API Key</h5>
<ol>
<li>Log in to your Perfex CRM admin panel</li>
<li>Go to <strong>API</strong> → <strong>API Keys</strong></li>
<li>Create a new API key or use an existing one</li>
<li>Copy the API key for use in your requests</li>
</ol>
<h5>Using Your API Key</h5>
<p>Include your API key in the Authorization header:</p>
<div class="code-block">
Authorization: Bearer YOUR_API_KEY_HERE
</div>
<h5>Example Request</h5>
<div class="code-block">
curl -X GET "https://yourdomain.com/api/clients" \
-H "Authorization: Bearer YOUR_API_KEY_HERE" \
-H "Content-Type: application/json"
</div>
</div>
</div>
<!-- API Endpoints Section -->
<div class="content-section" id="endpoints">
<div class="section-header">
<h4><i class="fas fa-code"></i> API Endpoints</h4>
</div>
<div class="section-body">
<p>All API endpoints follow the pattern: <code>https://yourdomain.com/api/{resource}</code></p>
<h5>Available Resources</h5>
<div class="row">
<div class="col-md-6">
<div class="endpoint-item">
<span class="method-badge method-get">GET</span>
<strong>/api/clients</strong>
<p class="mb-0">Retrieve all clients</p>
</div>
<div class="endpoint-item">
<span class="method-badge method-post">POST</span>
<strong>/api/clients</strong>
<p class="mb-0">Create a new client</p>
</div>
<div class="endpoint-item">
<span class="method-badge method-get">GET</span>
<strong>/api/projects</strong>
<p class="mb-0">Retrieve all projects</p>
</div>
<div class="endpoint-item">
<span class="method-badge method-post">POST</span>
<strong>/api/projects</strong>
<p class="mb-0">Create a new project</p>
</div>
</div>
<div class="col-md-6">
<div class="endpoint-item">
<span class="method-badge method-get">GET</span>
<strong>/api/leads</strong>
<p class="mb-0">Retrieve all leads</p>
</div>
<div class="endpoint-item">
<span class="method-badge method-post">POST</span>
<strong>/api/leads</strong>
<p class="mb-0">Create a new lead</p>
</div>
<div class="endpoint-item">
<span class="method-badge method-get">GET</span>
<strong>/api/tickets</strong>
<p class="mb-0">Retrieve all tickets</p>
</div>
<div class="endpoint-item">
<span class="method-badge method-post">POST</span>
<strong>/api/tickets</strong>
<p class="mb-0">Create a new ticket</p>
</div>
</div>
</div>
<h5>Response Format</h5>
<p>All API responses are returned in JSON format with the following structure:</p>
<div class="code-block">
{
"status": true,
"message": "Success",
"data": {
// Response data here
}
}
</div>
</div>
</div>
<!-- Examples Section -->
<div class="content-section" id="examples">
<div class="section-header">
<h4><i class="fas fa-lightbulb"></i> Examples</h4>
</div>
<div class="section-body">
<h5>Create a New Lead</h5>
<div class="code-block">
curl -X POST "https://yourdomain.com/api/leads" \
-H "Authorization: Bearer YOUR_API_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"name": "John Doe",
"email": "john@example.com",
"phone": "+1234567890",
"company": "Example Corp",
"source": "Website",
"status": "New"
}'
</div>
<h5>Get All Clients</h5>
<div class="code-block">
curl -X GET "https://yourdomain.com/api/clients" \
-H "Authorization: Bearer YOUR_API_KEY_HERE" \
-H "Content-Type: application/json"
</div>
<h5>Create a New Ticket</h5>
<div class="code-block">
curl -X POST "https://yourdomain.com/api/tickets" \
-H "Authorization: Bearer YOUR_API_KEY_HERE" \
-H "Content-Type: application/json" \
-d '{
"subject": "API Test Ticket",
"message": "This is a test ticket created via API",
"department": "Support",
"priority": "Medium",
"status": "Open"
}'
</div>
</div>
</div>
<!-- Playground Section -->
<div class="content-section" id="playground">
<div class="section-header">
<h4><i class="fas fa-flask"></i> API Playground</h4>
</div>
<div class="section-body">
<p>Test the API directly in your browser using our interactive playground:</p>
<a href="<?php echo base_url('api/playground'); ?>" class="btn btn-primary">
<i class="fas fa-play"></i> Open API Playground
</a>
<a href="<?php echo base_url('api/playground/swagger'); ?>" class="btn btn-outline-primary ms-2">
<i class="fas fa-code"></i> View Swagger UI
</a>
</div>
</div>
<!-- Footer -->
<div class="text-center mt-4">
<p class="text-muted">
<a href="https://perfexcrm.themesic.com/apiguide/" target="_blank" class="btn btn-outline-primary">
<i class="fas fa-external-link-alt"></i> View Full API Documentation
</a>
</p>
</div>
</div>
<!-- Scripts -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://kit.fontawesome.com/your-fontawesome-kit.js" crossorigin="anonymous"></script>
<script>
// Smooth scrolling for navigation links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,120 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo $title; ?></title>
<link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@4.15.5/swagger-ui.css" />
<style>
body {
margin: 0;
padding: 0;
background-color: #f8f9fa;
}
.swagger-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px 0;
text-align: center;
}
.swagger-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.swagger-ui {
background: white;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
overflow: hidden;
}
.back-button {
position: absolute;
top: 20px;
left: 20px;
background: rgba(255,255,255,0.2);
border: 1px solid rgba(255,255,255,0.3);
color: white;
padding: 8px 16px;
border-radius: 5px;
text-decoration: none;
transition: all 0.3s ease;
}
.back-button:hover {
background: rgba(255,255,255,0.3);
color: white;
text-decoration: none;
}
</style>
</head>
<body>
<!-- Header -->
<div class="swagger-header">
<h1><i class="fas fa-code"></i> Perfex CRM API - Swagger UI</h1>
<p class="lead">Interactive API documentation and testing</p>
</div>
<!-- Swagger UI Container -->
<div class="swagger-container">
<div class="swagger-ui">
<div id="swagger-ui"></div>
</div>
</div>
<!-- Scripts -->
<script src="https://unpkg.com/swagger-ui-dist@4.15.5/swagger-ui-bundle.js"></script>
<script src="https://unpkg.com/swagger-ui-dist@4.15.5/swagger-ui-standalone-preset.js"></script>
<script src="https://kit.fontawesome.com/your-fontawesome-kit.js" crossorigin="anonymous"></script>
<style>
.swagger-ui > div:nth-child(2) > .wrapper:nth-child(5) {
display: none;
}
</style>
<script>
// Swagger UI configuration
window.onload = function() {
const ui = SwaggerUIBundle({
url: '<?php echo base_url('api/playground/swagger'); ?>',
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout",
tryItOutEnabled: true,
requestInterceptor: function(request) {
// Add base URL to requests
if (request.url.startsWith('/api/')) {
request.url = '<?php echo base_url(); ?>' + request.url;
}
return request;
},
onComplete: function() {
// Add authentication button
const authButton = document.createElement('button');
authButton.innerHTML = '<i class="fas fa-key"></i> Set API Key';
authButton.className = 'btn btn-primary';
authButton.style.margin = '10px';
authButton.onclick = function() {
const apiKey = prompt('Enter your API key:');
if (apiKey) {
// Set the API key in Swagger UI
ui.preauthorizeApiKey('Bearer', apiKey);
alert('API key set successfully!');
}
};
const topbar = document.querySelector('.swagger-ui .topbar');
if (topbar) {
topbar.appendChild(authButton);
}
}
});
};
</script>
</body>
</html>

224
api/views/reporting.php Normal file
View File

@@ -0,0 +1,224 @@
<?php defined('BASEPATH') or exit('No direct script access allowed'); ?>
<?php init_head(); ?>
<link href="<?php echo base_url('modules/api/assets/main.css'); ?>" rel="stylesheet" type="text/css" />
<div id="wrapper">
<div class="content">
<div class="row">
<div class="col-md-12">
<div class="panel_s">
<div class="panel-body padding-10">
<div class="tw-flex tw-justify-between tw-items-center tw-p-1.5">
<p class="tw-font-semibold tw-flex tw-items-center tw-mb-0 tw-space-x-1.5 rtl:tw-space-x-reverse">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="tw-w-6 tw-h-6 tw-text-neutral-500">
<path stroke-linecap="round" stroke-linejoin="round" d="M10.125 2.25h-4.5c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125v-9M10.125 2.25h.375a9 9 0 019 9v.375M10.125 2.25A3.375 3.375 0 0113.5 5.625v1.5c0 .621.504 1.125 1.125 1.125h1.5a3.375 3.375 0 013.375 3.375M9 15l2.25 2.25L15 12" />
</svg>
<span class="tw-text-neutral-700">
<?php echo _l('all_apis'); ?>
</span>
</p>
<div class="tw-divide-x tw-divide-solid tw-divide-neutral-300 tw-space-x-2 tw-flex tw-items-center">
<div class="dropdown pull-right mright10">
<a href="#" id="ApisChartMode" class="dropdown-toggle tw-pl-2" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span id="Api-chart-mode" data-active-chart="hourly">
<?php echo _l('hourly') ?>
</span>
<i class="fa fa-caret-down" aria-hidden="true"></i>
</a>
<ul class="dropdown-menu dropdown-menu-right" aria-labelledby="ApisChartMode">
<li>
<a href="#" data-mode="hourly" onclick="update_apis_statistics(this); return false;">
<?php echo _l('hourly') ?>
</a>
</li>
<li>
<a href="#" data-mode="daily" onclick="update_apis_statistics(this); return false;">
<?php echo _l('daily') ?>
</a>
</li>
<li>
<a href="#" data-mode="weekly" onclick="update_apis_statistics(this); return false;">
<?php echo _l('weekly') ?>
</a>
</li>
<li>
<a href="#" data-mode="monthly" onclick="update_apis_statistics(this); return false;">
<?php echo _l('monthly') ?>
</a>
</li>
</ul>
</div>
</div>
</div>
<hr class="-tw-mx-3 tw-mt-2 tw-mb-4">
<canvas height="130" class="all-apis-chart" id="all-apis-chart"></canvas>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="panel_s">
<div class="panel-body padding-10">
<div class="tw-flex tw-justify-between tw-items-center tw-p-1.5">
<p class="tw-font-semibold tw-flex tw-items-center tw-mb-0 tw-space-x-1.5 rtl:tw-space-x-reverse">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="tw-w-6 tw-h-6 tw-text-neutral-500">
<path stroke-linecap="round" stroke-linejoin="round" d="M10.125 2.25h-4.5c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125v-9M10.125 2.25h.375a9 9 0 019 9v.375M10.125 2.25A3.375 3.375 0 0113.5 5.625v1.5c0 .621.504 1.125 1.125 1.125h1.5a3.375 3.375 0 013.375 3.375M9 15l2.25 2.25L15 12" />
</svg>
<span class="tw-text-neutral-700">
<?php echo _l('endpoints'); ?>
</span>
</p>
<div class="tw-divide-x tw-divide-solid tw-divide-neutral-300 tw-space-x-2 tw-flex tw-items-center">
<div class="dropdown pull-right mright10">
<a href="#" id="EndpointsChartUri" class="dropdown-toggle tw-pl-2" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span id="Endpoint-chart-uri" data-active-chart="<?php if (count($endpoints)) { echo $endpoints[0]; } ?>">
<?php if (count($endpoints)) { echo $endpoints[0]; } ?>
</span>
<i class="fa fa-caret-down" aria-hidden="true"></i>
</a>
<ul class="dropdown-menu dropdown-menu-right" aria-labelledby="EndpointsChartUri">
<?php foreach ($endpoints as $endpoint) { ?>
<li>
<a href="#" data-uri="<?php echo $endpoint ?>" onclick="update_endpoints_statistics(this); return false;">
<?php echo $endpoint ?>
</a>
</li>
<?php } ?>
</ul>
</div>
<div class="dropdown pull-right mright10">
<a href="#" id="EndpointsChartMode" class="dropdown-toggle tw-pl-2" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span id="Endpoint-chart-mode" data-active-chart="hourly">
<?php echo _l('hourly') ?>
</span>
<i class="fa fa-caret-down" aria-hidden="true"></i>
</a>
<ul class="dropdown-menu dropdown-menu-right" aria-labelledby="EndpointsChartMode">
<li>
<a href="#" data-mode="hourly" onclick="update_endpoints_statistics(null, this); return false;">
<?php echo _l('hourly') ?>
</a>
</li>
<li>
<a href="#" data-mode="daily" onclick="update_endpoints_statistics(null, this); return false;">
<?php echo _l('daily') ?>
</a>
</li>
<li>
<a href="#" data-mode="weekly" onclick="update_endpoints_statistics(null, this); return false;">
<?php echo _l('weekly') ?>
</a>
</li>
<li>
<a href="#" data-mode="monthly" onclick="update_endpoints_statistics(null, this); return false;">
<?php echo _l('monthly') ?>
</a>
</li>
</ul>
</div>
</div>
</div>
<hr class="-tw-mx-3 tw-mt-2 tw-mb-4">
<canvas height="130" class="all-endpoints-chart" id="all-endpoints-chart"></canvas>
</div>
</div>
</div>
</div>
</div>
</div>
<?php init_tail(); ?>
<script src="<?php echo base_url('modules/api/assets/main.js'); ?>"></script>
<script>
var apis_statistics;
var endpoints_statistics;
function update_apis_statistics(el) {
let mode = $(el).data('mode');
let $chartNameWrapper = $('#Api-chart-mode');
$chartNameWrapper.data('active-chart', mode);
$chartNameWrapper.text($(el).text());
if (typeof(apis_statistics) !== 'undefined') {
apis_statistics.destroy();
}
$.get(admin_url + 'api/statistics/' + mode, function(response) {
apis_statistics = new Chart($('#all-apis-chart'), {
type: 'bar',
data: response,
options: {
responsive: true,
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
}
}]
},
},
});
}, 'json');
}
function update_endpoints_statistics(uri_el, mode_el = null) {
console.log("Update Endpoints");
let uri, mode;
if (uri_el) {
uri = $(uri_el).data('uri');
let $chartUriWrapper = $('#Endpoint-chart-uri');
$chartUriWrapper.data('active-chart', uri);
$chartUriWrapper.text($(uri_el).text());
} else {
uri = $('#Endpoint-chart-uri').data("active-chart");
}
if (mode_el) {
mode = $(mode_el).data('mode');
let $chartModeWrapper = $('#Endpoint-chart-mode');
$chartModeWrapper.data('active-chart', mode);
$chartModeWrapper.text($(mode_el).text());
} else {
mode = $('#Endpoint-chart-mode').data("active-chart");
}
if (typeof(endpoints_statistics) !== 'undefined') {
endpoints_statistics.destroy();
}
$.get(admin_url + 'api/statistics/' + mode + "/" + encodeURIComponent(uri), function(response) {
endpoints_statistics = new Chart($('#all-endpoints-chart'), {
type: 'bar',
data: response,
options: {
responsive: true,
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
}
}]
},
},
});
}, 'json');
}
$(document).ready(function() {
update_apis_statistics($('[aria-labelledby="ApisChartMode"] [data-mode="hourly"]'));
update_endpoints_statistics($('[aria-labelledby="EndpointsChartUri"] li:first-child a'));
});
</script>

33
api/views/table.php Normal file
View File

@@ -0,0 +1,33 @@
<?php
defined('BASEPATH') or exit('No direct script access allowed');
$aColumns = [
'name',
];
$sIndexColumn = 'roleid';
$sTable = db_prefix().'roles';
$result = data_tables_init($aColumns, $sIndexColumn, $sTable, [], [], ['roleid']);
$output = $result['output'];
$rResult = $result['rResult'];
foreach ($rResult as $aRow) {
$row = [];
for ($i = 0; $i < count($aColumns); $i++) {
$_data = $aRow[$aColumns[$i]];
if ($aColumns[$i] == 'name') {
$_data = '<a href="' . admin_url('roles/role/' . $aRow['roleid']) . '" class="mbot10 display-block">' . $_data . '</a>';
$_data .= '<span class="mtop10 display-block">' . _l('roles_total_users') . ' ' . total_rows(db_prefix().'staff', [
'role' => $aRow['roleid'],
]) . '</span>';
}
$row[] = $_data;
}
$options = icon_btn('roles/role/' . $aRow['roleid'], 'pencil-square-o');
$row[] = $options .= icon_btn('roles/delete/' . $aRow['roleid'], 'remove', 'btn-danger _delete');
$output['aaData'][] = $row;
}

280
api/views/user_stats.php Normal file
View File

@@ -0,0 +1,280 @@
<?php defined('BASEPATH') or exit('No direct script access allowed'); ?>
<?php init_head(); ?>
<link href="<?php echo base_url('modules/api/assets/main.css'); ?>" rel="stylesheet" type="text/css" />
<script src="https://code.highcharts.com/highcharts.js"></script>
<div id="wrapper">
<div class="content">
<div class="row">
<div class="col-md-12">
<div class="panel_s">
<div class="panel-body">
<div class="row">
<div class="col-md-12">
<h4 class="no-margin"><?php echo _l('user_statistics'); ?></h4>
<hr class="hr-panel-heading" />
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="user_select" class="control-label"><?php echo _l('select_api_user'); ?></label>
<select class="form-control" id="user_select" name="user_id">
<option value=""><?php echo _l('select_api_user'); ?></option>
<?php foreach ($api_users as $user) { ?>
<option value="<?php echo $user['id']; ?>" <?php echo ($user_id == $user['id']) ? 'selected' : ''; ?>>
<?php echo $user['name'] . ' (' . $user['user'] . ')'; ?>
</option>
<?php } ?>
</select>
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label for="days_select" class="control-label"><?php echo _l('time_period'); ?></label>
<select class="form-control" id="days_select" name="days">
<option value="7"><?php echo _l('last_7_days'); ?></option>
<option value="30" selected><?php echo _l('last_30_days'); ?></option>
<option value="90"><?php echo _l('last_90_days'); ?></option>
</select>
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label class="control-label">&nbsp;</label>
<button type="button" id="load_stats" class="btn btn-primary btn-block"><?php echo _l('load_statistics'); ?></button>
</div>
</div>
</div>
<div id="stats_content" style="display: none;">
<!-- Quota Summary -->
<div class="row" id="quota_summary">
<div class="col-md-12">
<h5><?php echo _l('quota_summary'); ?></h5>
<div class="row">
<div class="col-md-3">
<div class="panel_s">
<div class="panel-body text-center">
<h3 class="text-primary" id="total_requests">0</h3>
<p class="text-muted"><?php echo _l('total_requests'); ?></p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="panel_s">
<div class="panel-body text-center">
<h3 class="text-success" id="success_requests">0</h3>
<p class="text-muted"><?php echo _l('success_requests'); ?></p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="panel_s">
<div class="panel-body text-center">
<h3 class="text-danger" id="error_requests">0</h3>
<p class="text-muted"><?php echo _l('error_requests'); ?></p>
</div>
</div>
</div>
<div class="col-md-3">
<div class="panel_s">
<div class="panel-body text-center">
<h3 class="text-info" id="avg_response_time">0ms</h3>
<p class="text-muted"><?php echo _l('avg_response_time'); ?></p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Usage Chart -->
<div class="row">
<div class="col-md-12">
<h5><?php echo _l('usage_over_time'); ?></h5>
<div id="usage_chart" style="height: 300px;"></div>
</div>
</div>
<!-- Top Endpoints -->
<div class="row">
<div class="col-md-12">
<h5><?php echo _l('top_endpoints'); ?></h5>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th><?php echo _l('endpoint'); ?></th>
<th><?php echo _l('request_count'); ?></th>
<th><?php echo _l('success_count'); ?></th>
<th><?php echo _l('error_count'); ?></th>
<th><?php echo _l('avg_response_time'); ?></th>
</tr>
</thead>
<tbody id="top_endpoints_table">
<tr>
<td colspan="5" class="text-center"><?php echo _l('no_data_available'); ?></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div id="no_user_selected" class="text-center" style="display: none;">
<p class="text-muted"><?php echo _l('select_api_user_to_view_statistics'); ?></p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<?php init_tail(); ?>
<script>
// Ensure jQuery is available
(function() {
function initUserStats() {
if (typeof jQuery === 'undefined') {
console.error('jQuery is not loaded. Retrying in 100ms...');
setTimeout(initUserStats, 100);
return;
}
jQuery(document).ready(function($) {
// Load stats if user is pre-selected
<?php if ($user_id) { ?>
loadUserStats();
<?php } ?>
$('#load_stats').click(function() {
loadUserStats();
});
function loadUserStats() {
var userId = $('#user_select').val();
var days = $('#days_select').val();
if (!userId) {
$('#stats_content').hide();
$('#no_user_selected').show();
return;
}
$('#no_user_selected').hide();
$('#stats_content').show();
$.ajax({
url: '<?php echo admin_url('api/get_user_stats_data'); ?>',
type: 'POST',
data: {
user_id: userId,
days: days
},
dataType: 'json',
success: function(response) {
if (response.error) {
alert('<?php echo _l('error_loading_statistics'); ?>');
return;
}
// Update quota summary
if (response.quota_summary) {
$('#total_requests').text(response.quota_summary.total_requests || 0);
$('#success_requests').text(response.quota_summary.success_requests || 0);
$('#error_requests').text(response.quota_summary.error_requests || 0);
$('#avg_response_time').text((response.quota_summary.avg_response_time || 0) + 'ms');
}
// Update top endpoints table
updateTopEndpointsTable(response.top_endpoints || []);
// Draw usage chart
drawUsageChart(response.quota_stats || []);
},
error: function() {
alert('<?php echo _l('error_loading_statistics'); ?>');
}
});
}
function updateTopEndpointsTable(endpoints) {
var tbody = $('#top_endpoints_table');
tbody.empty();
if (endpoints.length === 0) {
tbody.append('<tr><td colspan="5" class="text-center"><?php echo _l('no_data_available'); ?></td></tr>');
return;
}
endpoints.forEach(function(endpoint) {
tbody.append(
'<tr>' +
'<td>' + endpoint.endpoint + '</td>' +
'<td>' + endpoint.request_count + '</td>' +
'<td>' + endpoint.success_count + '</td>' +
'<td>' + endpoint.error_count + '</td>' +
'<td>' + endpoint.avg_response_time + 'ms</td>' +
'</tr>'
);
});
}
function drawUsageChart(stats) {
if (typeof Highcharts === 'undefined') {
console.log('Highcharts not loaded');
return;
}
var categories = [];
var requests = [];
var errors = [];
stats.forEach(function(stat) {
categories.push(stat.date);
requests.push(parseInt(stat.request_count));
errors.push(parseInt(stat.error_count));
});
Highcharts.chart('usage_chart', {
chart: {
type: 'line'
},
title: {
text: '<?php echo _l('usage_over_time'); ?>'
},
xAxis: {
categories: categories
},
yAxis: {
title: {
text: '<?php echo _l('request_count'); ?>'
}
},
series: [{
name: '<?php echo _l('total_requests'); ?>',
data: requests,
color: '#1f77b4'
}, {
name: '<?php echo _l('error_requests'); ?>',
data: errors,
color: '#d62728'
}]
});
}
});
}
// Start the initialization
initUserStats();
})();
</script>
<script src="<?php echo base_url('modules/api/assets/main.js'); ?>"></script>