185 lines
6.2 KiB
PHP
185 lines
6.2 KiB
PHP
<?php
|
|
|
|
namespace App\Controllers\Share;
|
|
|
|
use App\Controllers\BaseController;
|
|
use App\Libraries\AuditLogger;
|
|
use App\Models\ChartModel;
|
|
use App\Models\DashboardModel;
|
|
use App\Models\SharedLinkModel;
|
|
|
|
/**
|
|
* Authenticated: create / revoke share links.
|
|
*/
|
|
class LinkController extends BaseController
|
|
{
|
|
public function generate()
|
|
{
|
|
$workspaceId = (int) $this->session->get('active_workspace_id');
|
|
$userId = (int) $this->session->get('user_id');
|
|
|
|
$rules = [
|
|
'type' => 'required|in_list[dashboard,chart]',
|
|
'resource_id' => 'required|integer',
|
|
'password' => 'permit_empty|max_length[200]',
|
|
'expires_at' => 'permit_empty|max_length[40]',
|
|
];
|
|
|
|
if (! $this->validate($rules)) {
|
|
if ($this->request->isAJAX()) {
|
|
return $this->response->setJSON(['success' => false, 'errors' => $this->validator->getErrors()])->setStatusCode(422);
|
|
}
|
|
|
|
return redirect()->back()->with('errors', $this->validator->getErrors());
|
|
}
|
|
|
|
$type = (string) $this->request->getPost('type');
|
|
$resourceId = (int) $this->request->getPost('resource_id');
|
|
|
|
if ($type === 'dashboard') {
|
|
$d = (new DashboardModel())->findForWorkspace($resourceId, $workspaceId);
|
|
if (! $d) {
|
|
return $this->ajaxOrRedirect(false, 'Dashboard not found.');
|
|
}
|
|
} else {
|
|
$c = (new ChartModel())->where('workspace_id', $workspaceId)->find($resourceId);
|
|
if (! $c) {
|
|
return $this->ajaxOrRedirect(false, 'Chart not found.');
|
|
}
|
|
}
|
|
|
|
$pwd = trim((string) $this->request->getPost('password'));
|
|
$hash = $pwd !== '' ? password_hash($pwd, PASSWORD_DEFAULT) : null;
|
|
|
|
$expRaw = trim((string) $this->request->getPost('expires_at'));
|
|
$expiresAt = null;
|
|
if ($expRaw !== '') {
|
|
$ts = strtotime($expRaw);
|
|
if ($ts === false) {
|
|
return $this->ajaxOrRedirect(false, 'Invalid expiry date.');
|
|
}
|
|
$expiresAt = date('Y-m-d H:i:s', $ts);
|
|
}
|
|
|
|
$model = new SharedLinkModel();
|
|
$token = SharedLinkModel::generateToken();
|
|
|
|
$id = (int) $model->insert([
|
|
'token' => $token,
|
|
'type' => $type,
|
|
'resource_id' => $resourceId,
|
|
'workspace_id' => $workspaceId,
|
|
'password_hash' => $hash,
|
|
'expires_at' => $expiresAt,
|
|
'view_count' => 0,
|
|
'is_active' => 1,
|
|
'created_by' => $userId,
|
|
], true);
|
|
|
|
$publicUrl = rtrim(base_url(), '/') . '/share/' . $token;
|
|
|
|
AuditLogger::log(
|
|
'share.created',
|
|
'shared_link',
|
|
$id,
|
|
null,
|
|
[
|
|
'type' => $type,
|
|
'resource_id' => $resourceId,
|
|
'has_password' => $hash !== null,
|
|
'expires_at' => $expiresAt,
|
|
],
|
|
$workspaceId,
|
|
$userId
|
|
);
|
|
|
|
if ($this->request->isAJAX()) {
|
|
return $this->response->setJSON([
|
|
'success' => true,
|
|
'id' => $id,
|
|
'token' => $token,
|
|
'url' => $publicUrl,
|
|
'view_count' => 0,
|
|
'csrf' => ['name' => csrf_token(), 'hash' => csrf_hash()],
|
|
]);
|
|
}
|
|
|
|
return redirect()->back()->with('success', 'Share link created: ' . $publicUrl);
|
|
}
|
|
|
|
public function revoke(int $id)
|
|
{
|
|
$workspaceId = (int) $this->session->get('active_workspace_id');
|
|
$model = new SharedLinkModel();
|
|
$row = $model->findOwned($id, $workspaceId);
|
|
if (! $row) {
|
|
return $this->ajaxOrRedirect(false, 'Link not found.');
|
|
}
|
|
|
|
AuditLogger::log(
|
|
'share.revoked',
|
|
'shared_link',
|
|
$id,
|
|
['is_active' => 1],
|
|
['is_active' => 0],
|
|
$workspaceId,
|
|
(int) $this->session->get('user_id')
|
|
);
|
|
|
|
$model->update($id, ['is_active' => 0]);
|
|
|
|
if ($this->request->isAJAX()) {
|
|
return $this->response->setJSON([
|
|
'success' => true,
|
|
'csrf' => ['name' => csrf_token(), 'hash' => csrf_hash()],
|
|
]);
|
|
}
|
|
|
|
return redirect()->back()->with('success', 'Share link revoked.');
|
|
}
|
|
|
|
/**
|
|
* Existing links for a resource (AJAX).
|
|
*/
|
|
public function listResource()
|
|
{
|
|
$workspaceId = (int) $this->session->get('active_workspace_id');
|
|
$type = (string) $this->request->getGet('type');
|
|
$resourceId = (int) $this->request->getGet('resource_id');
|
|
if (! in_array($type, ['dashboard', 'chart'], true) || $resourceId <= 0) {
|
|
return $this->response->setJSON(['success' => false, 'message' => 'Invalid parameters.'])->setStatusCode(422);
|
|
}
|
|
|
|
if ($type === 'dashboard') {
|
|
if (! (new DashboardModel())->findForWorkspace($resourceId, $workspaceId)) {
|
|
return $this->response->setJSON(['success' => false])->setStatusCode(404);
|
|
}
|
|
} elseif (! (new ChartModel())->where('workspace_id', $workspaceId)->find($resourceId)) {
|
|
return $this->response->setJSON(['success' => false])->setStatusCode(404);
|
|
}
|
|
|
|
$rows = (new SharedLinkModel())->forResource($type, $resourceId, $workspaceId);
|
|
$out = [];
|
|
foreach ($rows as $r) {
|
|
$out[] = [
|
|
'id' => (int) $r['id'],
|
|
'url' => rtrim(base_url(), '/') . '/share/' . $r['token'],
|
|
'view_count' => (int) ($r['view_count'] ?? 0),
|
|
'expires_at' => $r['expires_at'],
|
|
'has_password' => ! empty($r['password_hash']),
|
|
];
|
|
}
|
|
|
|
return $this->response->setJSON(['success' => true, 'links' => $out]);
|
|
}
|
|
|
|
private function ajaxOrRedirect(bool $ok, string $message)
|
|
{
|
|
if ($this->request->isAJAX()) {
|
|
return $this->response->setJSON(['success' => $ok, 'message' => $message])->setStatusCode($ok ? 200 : 404);
|
|
}
|
|
|
|
return redirect()->back()->with($ok ? 'success' : 'error', $message);
|
|
}
|
|
}
|