Initial commit: Modular Deploymentcenter platform
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Deploymentcenter\Modules\License;
|
||||
|
||||
use PDO;
|
||||
|
||||
class Audit
|
||||
{
|
||||
public static function log(PDO $db, string $actor, string $action, ?array $details = null): void
|
||||
{
|
||||
try {
|
||||
$stmt = $db->prepare('INSERT INTO license_audit_log (actor, action, details) VALUES (:actor, :action, :details)');
|
||||
$stmt->execute([
|
||||
':actor' => $actor,
|
||||
':action' => $action,
|
||||
':details' => $details !== null ? json_encode($details, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) : null,
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
// Ignore audit log failure
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Deploymentcenter\Modules\License;
|
||||
|
||||
class KeyGen
|
||||
{
|
||||
private const CHARSET = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ';
|
||||
|
||||
public static function generateKey(): string
|
||||
{
|
||||
$groups = [];
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
$group = '';
|
||||
for ($j = 0; $j < 5; $j++) {
|
||||
$group .= self::CHARSET[random_int(0, strlen(self::CHARSET) - 1)];
|
||||
}
|
||||
$groups[] = $group;
|
||||
}
|
||||
return implode('-', $groups);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
|
||||
namespace Deploymentcenter\Modules\License;
|
||||
|
||||
use PDO;
|
||||
|
||||
class LicenseService
|
||||
{
|
||||
private PDO $db;
|
||||
|
||||
public function __construct(PDO $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
public function validate(array $requestData, string $clientIp): array
|
||||
{
|
||||
$productSlug = trim($requestData['product'] ?? '');
|
||||
$licenseKey = trim($requestData['license_key'] ?? '');
|
||||
$hardwareId = trim($requestData['hardware_id'] ?? '');
|
||||
$nonce = trim($requestData['nonce'] ?? '');
|
||||
$hostname = trim($requestData['hostname'] ?? '');
|
||||
$appVersion = trim($requestData['app_version'] ?? '');
|
||||
|
||||
$issuedAt = time();
|
||||
$endpoints = $this->getEndpoints();
|
||||
$cacheTtlHours = 168;
|
||||
|
||||
$makePayload = function(string $status, ?array $extra = []) use ($issuedAt, $nonce, $productSlug, $licenseKey, $hardwareId, $endpoints, &$cacheTtlHours) {
|
||||
$base = [
|
||||
'type' => 'validation_result',
|
||||
'issued_at' => $issuedAt,
|
||||
'nonce' => $nonce,
|
||||
'product' => $productSlug,
|
||||
'license_key' => $licenseKey,
|
||||
'hardware_id' => $hardwareId,
|
||||
'status' => $status,
|
||||
'expires_at' => null,
|
||||
'cache_ttl_hours' => $cacheTtlHours,
|
||||
'endpoints' => $endpoints,
|
||||
'message' => null
|
||||
];
|
||||
return array_merge($base, $extra);
|
||||
};
|
||||
|
||||
// 1. Fetch Product
|
||||
$stmt = $this->db->prepare('SELECT id, default_cache_ttl_hours FROM license_products WHERE slug = :slug');
|
||||
$stmt->execute([':slug' => $productSlug]);
|
||||
$product = $stmt->fetch();
|
||||
if (!$product) {
|
||||
Audit::log($this->db, 'api', 'api.validate.unknown_product', [
|
||||
'product' => $productSlug,
|
||||
'key_prefix' => substr($licenseKey, 0, 5),
|
||||
'ip' => $clientIp
|
||||
]);
|
||||
return $makePayload('not_found', ['message' => 'Product not found']);
|
||||
}
|
||||
$cacheTtlHours = (int)$product['default_cache_ttl_hours'];
|
||||
|
||||
// 2. Fetch License
|
||||
$stmt = $this->db->prepare('SELECT * FROM license_licenses WHERE product_id = :pid AND license_key = :key');
|
||||
$stmt->execute([':pid' => $product['id'], ':key' => $licenseKey]);
|
||||
$license = $stmt->fetch();
|
||||
|
||||
if (!$license) {
|
||||
Audit::log($this->db, 'api', 'api.validate.failed_key', [
|
||||
'product' => $productSlug,
|
||||
'key_prefix' => substr($licenseKey, 0, 5),
|
||||
'ip' => $clientIp
|
||||
]);
|
||||
return $makePayload('not_found', ['message' => 'Invalid license key']);
|
||||
}
|
||||
|
||||
$expiresAtUnix = $license['expires_at'] ? strtotime($license['expires_at']) : null;
|
||||
|
||||
// 3. License status check
|
||||
if ($license['status'] === 'revoked') {
|
||||
return $makePayload('revoked', [
|
||||
'expires_at' => $expiresAtUnix,
|
||||
'message' => 'License has been revoked'
|
||||
]);
|
||||
}
|
||||
|
||||
if ($license['status'] === 'suspended') {
|
||||
return $makePayload('suspended', [
|
||||
'expires_at' => $expiresAtUnix,
|
||||
'message' => 'License is temporarily suspended'
|
||||
]);
|
||||
}
|
||||
|
||||
if ($expiresAtUnix !== null && $expiresAtUnix < $issuedAt) {
|
||||
return $makePayload('expired', [
|
||||
'expires_at' => $expiresAtUnix,
|
||||
'message' => 'License has expired'
|
||||
]);
|
||||
}
|
||||
|
||||
// 4. Activation management
|
||||
$stmt = $this->db->prepare('SELECT * FROM license_activations WHERE license_id = :lic_id AND hardware_id = :hw_id');
|
||||
$stmt->execute([':lic_id' => $license['id'], ':hw_id' => $hardwareId]);
|
||||
$activation = $stmt->fetch();
|
||||
|
||||
if ($activation) {
|
||||
if ((int)$activation['is_blocked'] === 1) {
|
||||
return $makePayload('revoked', [
|
||||
'expires_at' => $expiresAtUnix,
|
||||
'message' => 'This hardware activation is blocked'
|
||||
]);
|
||||
}
|
||||
$upd = $this->db->prepare('UPDATE license_activations SET last_seen = NOW(), hostname = :host, app_version = :ver WHERE id = :id');
|
||||
$upd->execute([':host' => $hostname, ':ver' => $appVersion, ':id' => $activation['id']]);
|
||||
} else {
|
||||
$cntStmt = $this->db->prepare('SELECT COUNT(*) FROM license_activations WHERE license_id = :lic_id AND is_blocked = 0');
|
||||
$cntStmt->execute([':lic_id' => $license['id']]);
|
||||
$activeCount = (int)$cntStmt->fetchColumn();
|
||||
|
||||
if ($activeCount >= (int)$license['max_activations']) {
|
||||
return $makePayload('activation_limit', [
|
||||
'expires_at' => $expiresAtUnix,
|
||||
'message' => 'Maximum activations reached for this license'
|
||||
]);
|
||||
}
|
||||
|
||||
$ins = $this->db->prepare('INSERT INTO license_activations (license_id, hardware_id, hostname, app_version) VALUES (:lic_id, :hw_id, :host, :ver)');
|
||||
$ins->execute([
|
||||
':lic_id' => $license['id'],
|
||||
':hw_id' => $hardwareId,
|
||||
':host' => $hostname,
|
||||
':ver' => $appVersion
|
||||
]);
|
||||
}
|
||||
|
||||
Audit::log($this->db, 'api', 'api.validate.success', [
|
||||
'license_id' => $license['id'],
|
||||
'hardware_id' => $hardwareId,
|
||||
'ip' => $clientIp
|
||||
]);
|
||||
|
||||
return $makePayload('valid', [
|
||||
'expires_at' => $expiresAtUnix,
|
||||
'message' => 'License is valid'
|
||||
]);
|
||||
}
|
||||
|
||||
public function deactivate(array $requestData, string $clientIp): array
|
||||
{
|
||||
$productSlug = trim($requestData['product'] ?? '');
|
||||
$licenseKey = trim($requestData['license_key'] ?? '');
|
||||
$hardwareId = trim($requestData['hardware_id'] ?? '');
|
||||
$nonce = trim($requestData['nonce'] ?? '');
|
||||
|
||||
$issuedAt = time();
|
||||
|
||||
$stmt = $this->db->prepare('
|
||||
SELECT a.id, a.license_id
|
||||
FROM license_activations a
|
||||
JOIN license_licenses l ON a.license_id = l.id
|
||||
JOIN license_products p ON l.product_id = p.id
|
||||
WHERE p.slug = :slug AND l.license_key = :key AND a.hardware_id = :hw_id
|
||||
');
|
||||
$stmt->execute([':slug' => $productSlug, ':key' => $licenseKey, ':hw_id' => $hardwareId]);
|
||||
$row = $stmt->fetch();
|
||||
|
||||
if ($row) {
|
||||
$del = $this->db->prepare('DELETE FROM license_activations WHERE id = :id');
|
||||
$del->execute([':id' => $row['id']]);
|
||||
|
||||
Audit::log($this->db, 'api', 'api.deactivate.success', [
|
||||
'license_id' => $row['license_id'],
|
||||
'hardware_id' => $hardwareId,
|
||||
'ip' => $clientIp
|
||||
]);
|
||||
}
|
||||
|
||||
return [
|
||||
'type' => 'deactivation_result',
|
||||
'issued_at' => $issuedAt,
|
||||
'nonce' => $nonce,
|
||||
'status' => 'ok',
|
||||
'message' => 'Activation deactivated successfully'
|
||||
];
|
||||
}
|
||||
|
||||
public function getEndpoints(): array
|
||||
{
|
||||
$stmt = $this->db->prepare("SELECT svalue FROM dc_settings WHERE skey = 'endpoints'");
|
||||
$stmt->execute();
|
||||
$val = $stmt->fetchColumn();
|
||||
if ($val) {
|
||||
$decoded = json_decode($val, true);
|
||||
if (is_array($decoded)) {
|
||||
return $decoded;
|
||||
}
|
||||
}
|
||||
return [
|
||||
'validate' => '/api/license/v1/validate',
|
||||
'deactivate' => '/api/license/v1/deactivate'
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace Deploymentcenter\Modules\License;
|
||||
|
||||
use PDO;
|
||||
|
||||
class RateLimiter
|
||||
{
|
||||
private PDO $db;
|
||||
private int $limit;
|
||||
private int $windowSeconds;
|
||||
|
||||
public function __construct(PDO $db, int $limit = 60, int $windowSeconds = 60)
|
||||
{
|
||||
$this->db = $db;
|
||||
$this->limit = $limit;
|
||||
$this->windowSeconds = $windowSeconds;
|
||||
}
|
||||
|
||||
public function check(string $ip): bool
|
||||
{
|
||||
$packedIp = inet_pton($ip);
|
||||
if ($packedIp === false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$now = time();
|
||||
$windowStart = date('Y-m-d H:i:s', $now - ($now % $this->windowSeconds));
|
||||
|
||||
$this->db->beginTransaction();
|
||||
|
||||
try {
|
||||
$stmt = $this->db->prepare('SELECT request_count FROM license_api_rate_limit WHERE ip = :ip AND window_start = :ws FOR UPDATE');
|
||||
$stmt->execute([':ip' => $packedIp, ':ws' => $windowStart]);
|
||||
$count = $stmt->fetchColumn();
|
||||
|
||||
if ($count === false) {
|
||||
$ins = $this->db->prepare('INSERT INTO license_api_rate_limit (ip, window_start, request_count) VALUES (:ip, :ws, 1)');
|
||||
$ins->execute([':ip' => $packedIp, ':ws' => $windowStart]);
|
||||
$this->db->commit();
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((int)$count >= $this->limit) {
|
||||
$this->db->commit();
|
||||
return false;
|
||||
}
|
||||
|
||||
$upd = $this->db->prepare('UPDATE license_api_rate_limit SET request_count = request_count + 1 WHERE ip = :ip AND window_start = :ws');
|
||||
$upd->execute([':ip' => $packedIp, ':ws' => $windowStart]);
|
||||
$this->db->commit();
|
||||
return true;
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
$this->db->rollBack();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Deploymentcenter\Modules\UpdateService;
|
||||
|
||||
use PDO;
|
||||
|
||||
class UpdateManager
|
||||
{
|
||||
private PDO $db;
|
||||
|
||||
public function __construct(PDO $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
public function checkUpdate(string $productSlug, string $currentVersion): ?array
|
||||
{
|
||||
$stmt = $this->db->prepare('
|
||||
SELECT * FROM updateservice_releases
|
||||
WHERE product_slug = :slug AND version > :ver
|
||||
ORDER BY created_at DESC LIMIT 1
|
||||
');
|
||||
$stmt->execute([':slug' => $productSlug, ':ver' => $currentVersion]);
|
||||
$latest = $stmt->fetch();
|
||||
|
||||
return $latest ?: null;
|
||||
}
|
||||
|
||||
public function addRelease(
|
||||
string $productSlug,
|
||||
string $version,
|
||||
?string $releaseNotes,
|
||||
string $downloadUrl,
|
||||
?string $sha256Hash,
|
||||
bool $isCritical = false
|
||||
): bool {
|
||||
$stmt = $this->db->prepare('
|
||||
INSERT INTO updateservice_releases (
|
||||
product_slug, version, release_notes, download_url, sha256_hash, is_critical
|
||||
) VALUES (
|
||||
:slug, :version, :notes, :url, :hash, :critical
|
||||
) ON DUPLICATE KEY UPDATE
|
||||
release_notes = VALUES(release_notes),
|
||||
download_url = VALUES(download_url),
|
||||
sha256_hash = VALUES(sha256_hash),
|
||||
is_critical = VALUES(is_critical)
|
||||
');
|
||||
|
||||
return $stmt->execute([
|
||||
':slug' => $productSlug,
|
||||
':version' => $version,
|
||||
':notes' => $releaseNotes,
|
||||
':url' => $downloadUrl,
|
||||
':hash' => $sha256Hash,
|
||||
':critical' => $isCritical ? 1 : 0,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getReleases(?string $productSlug = null): array
|
||||
{
|
||||
if ($productSlug) {
|
||||
$stmt = $this->db->prepare('SELECT * FROM updateservice_releases WHERE product_slug = :slug ORDER BY created_at DESC');
|
||||
$stmt->execute([':slug' => $productSlug]);
|
||||
} else {
|
||||
$stmt = $this->db->query('SELECT * FROM updateservice_releases ORDER BY created_at DESC');
|
||||
}
|
||||
return $stmt->fetchAll() ?: [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace Deploymentcenter\Modules\Watchdog;
|
||||
|
||||
use PDO;
|
||||
|
||||
class EventLog
|
||||
{
|
||||
private PDO $db;
|
||||
|
||||
public function __construct(PDO $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
public function logEvent(
|
||||
string $source,
|
||||
string $instance,
|
||||
string $kind,
|
||||
?string $fromState = null,
|
||||
?string $toState = null,
|
||||
string $severity = 'info',
|
||||
?string $message = null,
|
||||
$meta = null
|
||||
): int {
|
||||
$metaJson = is_array($meta) || is_object($meta) ? json_encode($meta, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) : null;
|
||||
$nowUtc = date('Y-m-d H:i:s');
|
||||
|
||||
$stmt = $this->db->prepare('
|
||||
INSERT INTO watchdog_event_log (
|
||||
source, instance, kind, from_state, to_state, severity, at_utc, message, meta_json
|
||||
) VALUES (
|
||||
:source, :instance, :kind, :from_state, :to_state, :severity, :now, :message, :meta
|
||||
)
|
||||
');
|
||||
|
||||
$stmt->execute([
|
||||
':source' => $source,
|
||||
':instance' => $instance,
|
||||
':kind' => $kind,
|
||||
':from_state' => $fromState,
|
||||
':to_state' => $toState,
|
||||
':severity' => $severity,
|
||||
':now' => $nowUtc,
|
||||
':message' => $message,
|
||||
':meta' => $metaJson,
|
||||
]);
|
||||
|
||||
return (int)$this->db->lastInsertId();
|
||||
}
|
||||
|
||||
public function getRecentEvents(int $limit = 50, ?string $source = null, ?string $instance = null): array
|
||||
{
|
||||
$sql = 'SELECT * FROM watchdog_event_log';
|
||||
$where = [];
|
||||
$params = [];
|
||||
|
||||
if ($source !== null) {
|
||||
$where[] = 'source = :source';
|
||||
$params[':source'] = $source;
|
||||
}
|
||||
if ($instance !== null) {
|
||||
$where[] = 'instance = :instance';
|
||||
$params[':instance'] = $instance;
|
||||
}
|
||||
|
||||
if (!empty($where)) {
|
||||
$sql .= ' WHERE ' . implode(' AND ', $where);
|
||||
}
|
||||
|
||||
$sql .= ' ORDER BY at_utc DESC LIMIT ' . (int)$limit;
|
||||
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
return $stmt->fetchAll() ?: [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace Deploymentcenter\Modules\Watchdog;
|
||||
|
||||
use PDO;
|
||||
|
||||
class MonitorRepo
|
||||
{
|
||||
private PDO $db;
|
||||
|
||||
public function __construct(PDO $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
public function getAllMonitors(): array
|
||||
{
|
||||
$stmt = $this->db->query('SELECT * FROM watchdog_monitors ORDER BY group_key ASC, source ASC');
|
||||
return $stmt->fetchAll() ?: [];
|
||||
}
|
||||
|
||||
public function getMonitor(string $source, string $instance = 'default'): ?array
|
||||
{
|
||||
$stmt = $this->db->prepare('SELECT * FROM watchdog_monitors WHERE source = :s AND instance = :i');
|
||||
$stmt->execute([':s' => $source, ':i' => $instance]);
|
||||
$row = $stmt->fetch();
|
||||
return $row ?: null;
|
||||
}
|
||||
|
||||
public function upsertHeartbeat(
|
||||
string $source,
|
||||
string $instance,
|
||||
string $type,
|
||||
int $intervalSec,
|
||||
$metrics,
|
||||
string $status,
|
||||
?string $message,
|
||||
?string $groupKey = null,
|
||||
?string $os = null
|
||||
): array {
|
||||
$nowUtc = date('Y-m-d H:i:s');
|
||||
$metricsJson = is_array($metrics) || is_object($metrics) ? json_encode($metrics, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) : null;
|
||||
$state = ($status === 'ok') ? 'up' : (($status === 'warning') ? 'warning' : 'down');
|
||||
|
||||
$stmt = $this->db->prepare('
|
||||
INSERT INTO watchdog_monitors (
|
||||
source, instance, type, state, expected_interval_sec, last_seen_utc,
|
||||
last_status, last_message, metrics_json, group_key, os, created_utc, updated_utc
|
||||
) VALUES (
|
||||
:source, :instance, :type, :state, :interval, :now,
|
||||
:last_status, :message, :metrics, :group_key, :os, :now, :now
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
state = VALUES(state),
|
||||
expected_interval_sec = VALUES(expected_interval_sec),
|
||||
last_seen_utc = VALUES(last_seen_utc),
|
||||
last_status = VALUES(last_status),
|
||||
last_message = VALUES(last_message),
|
||||
metrics_json = VALUES(metrics_json),
|
||||
group_key = COALESCE(VALUES(group_key), group_key),
|
||||
os = COALESCE(VALUES(os), os),
|
||||
updated_utc = VALUES(updated_utc)
|
||||
');
|
||||
|
||||
$stmt->execute([
|
||||
':source' => $source,
|
||||
':instance' => $instance,
|
||||
':type' => $type,
|
||||
':state' => $state,
|
||||
':interval' => $intervalSec,
|
||||
':now' => $nowUtc,
|
||||
':last_status' => $status,
|
||||
':message' => $message,
|
||||
':metrics' => $metricsJson,
|
||||
':group_key' => $groupKey,
|
||||
':os' => $os,
|
||||
]);
|
||||
|
||||
return $this->getMonitor($source, $instance);
|
||||
}
|
||||
|
||||
public function updateState(int $id, string $state, ?string $reason = null): bool
|
||||
{
|
||||
$stmt = $this->db->prepare('UPDATE watchdog_monitors SET state = :state, metric_state_reason = :reason, updated_utc = NOW() WHERE id = :id');
|
||||
return $stmt->execute([':state' => $state, ':reason' => $reason, ':id' => $id]);
|
||||
}
|
||||
|
||||
public function setMaintenance(string $source, string $instance, ?string $untilUtc): bool
|
||||
{
|
||||
$state = $untilUtc ? 'maintenance' : 'up';
|
||||
$stmt = $this->db->prepare('UPDATE watchdog_monitors SET state = :state, suppress_until_utc = :until, updated_utc = NOW() WHERE source = :s AND instance = :i');
|
||||
return $stmt->execute([':state' => $state, ':until' => $untilUtc, ':s' => $source, ':i' => $instance]);
|
||||
}
|
||||
|
||||
public function deleteMonitor(string $source, string $instance = 'default'): bool
|
||||
{
|
||||
$stmt = $this->db->prepare('DELETE FROM watchdog_monitors WHERE source = :s AND instance = :i');
|
||||
return $stmt->execute([':s' => $source, ':i' => $instance]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace Deploymentcenter\Modules\Watchdog;
|
||||
|
||||
use PDO;
|
||||
|
||||
class TokenManager
|
||||
{
|
||||
private PDO $db;
|
||||
|
||||
public function __construct(PDO $db)
|
||||
{
|
||||
$this->db = $db;
|
||||
}
|
||||
|
||||
public function createToken(string $source, string $name, string $notes = ''): array
|
||||
{
|
||||
$tokenId = 'tok_' . bin2hex(random_bytes(8));
|
||||
$rawToken = 'wd_' . bin2hex(random_bytes(24));
|
||||
$tokenHash = hash('sha256', $rawToken);
|
||||
|
||||
$stmt = $this->db->prepare('
|
||||
INSERT INTO watchdog_agent_tokens (
|
||||
token_id, token_hash, name, monitor_source, created_at_utc
|
||||
) VALUES (
|
||||
:id, :hash, :name, :source, NOW()
|
||||
)
|
||||
');
|
||||
|
||||
$stmt->execute([
|
||||
':id' => $tokenId,
|
||||
':hash' => $tokenHash,
|
||||
':name' => $name,
|
||||
':source' => $source,
|
||||
]);
|
||||
|
||||
return [
|
||||
'token_id' => $tokenId,
|
||||
'raw_token' => $rawToken,
|
||||
];
|
||||
}
|
||||
|
||||
public function validateToken(string $rawToken, string $targetSource): bool
|
||||
{
|
||||
$hash = hash('sha256', $rawToken);
|
||||
$stmt = $this->db->prepare('SELECT * FROM watchdog_agent_tokens WHERE token_hash = :hash AND revoked = 0');
|
||||
$stmt->execute([':hash' => $hash]);
|
||||
$row = $stmt->fetch();
|
||||
|
||||
if (!$row) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!empty($row['monitor_source']) && $row['monitor_source'] !== $targetSource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$upd = $this->db->prepare('UPDATE watchdog_agent_tokens SET last_used_at_utc = NOW() WHERE token_id = :id');
|
||||
$upd->execute([':id' => $row['token_id']]);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user