[verified] Harden Etsy MCP validation and scheduled token refresh
Deploy HickLab3D Dashboard / Deploy to production (push) Successful in 38s

This commit is contained in:
2026-11-07 21:54:33 +00:00
parent 960189d14b
commit c8c5822166
13 changed files with 455 additions and 89 deletions
+21 -6
View File
@@ -26,7 +26,7 @@ class DashboardSchedule
'name' => 'Integration cache refresh',
'feature' => 'Integrations',
'command' => 'integrations:refresh',
'description' => 'Refreshes cached Etsy, Parcel2Go, BamBuddy and dryer data so dashboard pages load instantly. Migrated from the previous hard-coded schedule entry — frequency and withoutOverlapping(5) behavior unchanged.',
'description' => 'Refreshes cached Parcel2Go, BamBuddy and dryer data. Etsy runs separately through etsy:sync; the 30-second cadence and withoutOverlapping(5) guard are unchanged.',
'default_frequency' => 'every_30_seconds',
'overlap_minutes' => 5,
'manual' => true,
@@ -46,16 +46,16 @@ class DashboardSchedule
'command' => 'etsy:sync',
'description' => 'Forces a fresh pull of Etsy receipts and active listings into the same IntegrationCache entries the Etsy page reads, independent of the bundled integrations:refresh job so it can be tuned or disabled on its own.',
'default_frequency' => 'every_15_minutes',
'overlap_minutes' => null,
'overlap_minutes' => 5,
'manual' => true,
],
'etsy_token_refresh' => [
'name' => 'Etsy token refresh',
'feature' => 'Etsy',
'command' => 'etsy:refresh-token',
'description' => 'Proactively refreshes the Etsy OAuth access token (tokens last ~1 hour) so the connection stays alive even during quiet periods with no receipts or listings activity to trigger an on-demand refresh.',
'default_frequency' => 'every_45_minutes',
'overlap_minutes' => null,
'description' => 'Checks every five minutes by default and refreshes only when expiry is within ten minutes. Disabling or slowing this job can leave idle tokens expired; on-demand reads also refresh expired tokens.',
'default_frequency' => 'every_5_minutes',
'overlap_minutes' => 5,
'manual' => true,
],
];
@@ -104,7 +104,22 @@ class DashboardSchedule
->all();
File::ensureDirectoryExists(dirname(self::runtimeSettingsPath()));
File::put(self::runtimeSettingsPath(), json_encode($payload, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
$lock = fopen(self::runtimeSettingsPath().'.lock', 'c');
if ($lock === false) {
throw new \RuntimeException('Unable to open schedule settings lock.');
}
try {
if (! flock($lock, LOCK_EX)) {
throw new \RuntimeException('Unable to lock schedule settings.');
}
$stored = File::exists(self::runtimeSettingsPath())
? json_decode(File::get(self::runtimeSettingsPath()), true, 512, JSON_THROW_ON_ERROR)
: [];
File::replace(self::runtimeSettingsPath(), json_encode(array_replace(is_array($stored) ? $stored : [], $payload), JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR));
} finally {
flock($lock, LOCK_UN);
fclose($lock);
}
}
public static function register(): void