| import { getConsistentMachineId } from "@/shared/utils/machineId";
|
| import { isCloudEnabled } from "@/lib/localDb";
|
| import { getRuntimePorts } from "@/lib/runtime/ports";
|
|
|
| const { dashboardPort } = getRuntimePorts();
|
|
|
| const INTERNAL_BASE_URL =
|
| process.env.BASE_URL ||
|
| process.env.NEXT_PUBLIC_BASE_URL ||
|
| process.env.NEXT_PUBLIC_APP_URL ||
|
| `http://localhost:${dashboardPort}`;
|
|
|
| |
| |
|
|
| export class CloudSyncScheduler {
|
| machineId: string | null;
|
| intervalMinutes: number;
|
| intervalId: ReturnType<typeof setInterval> | null;
|
|
|
| constructor(machineId: string | null = null, intervalMinutes = 15) {
|
| this.machineId = machineId;
|
| this.intervalMinutes = intervalMinutes;
|
| this.intervalId = null;
|
| }
|
|
|
| |
| |
|
|
| async initializeMachineId() {
|
| if (!this.machineId) {
|
| this.machineId = await getConsistentMachineId();
|
| }
|
| }
|
|
|
| |
| |
|
|
| async start() {
|
| if (this.intervalId) {
|
| return;
|
| }
|
|
|
| await this.initializeMachineId();
|
|
|
|
|
| const startupTimer = setTimeout(() => {
|
| this.syncWithRetry().catch(() => {});
|
| }, 30000);
|
| startupTimer.unref?.();
|
|
|
|
|
| this.intervalId = setInterval(
|
| () => {
|
| this.syncWithRetry().catch(() => {});
|
| },
|
| this.intervalMinutes * 60 * 1000
|
| );
|
| this.intervalId.unref?.();
|
| }
|
|
|
| |
| |
|
|
| stop() {
|
| if (this.intervalId) {
|
| clearInterval(this.intervalId);
|
| this.intervalId = null;
|
| }
|
| }
|
|
|
| |
| |
|
|
| async syncWithRetry(maxRetries = 1) {
|
| for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
| try {
|
| const result = await this.sync();
|
| return result;
|
| } catch (error) {
|
| if (attempt === maxRetries) {
|
| return null;
|
| }
|
|
|
| const delay = Math.min(1000 * Math.pow(2, attempt), 10000);
|
| await new Promise((resolve) => setTimeout(resolve, delay));
|
| }
|
| }
|
| }
|
|
|
| |
| |
|
|
| async sync() {
|
|
|
| const enabled = await isCloudEnabled();
|
| if (!enabled) {
|
| return null;
|
| }
|
|
|
| await this.initializeMachineId();
|
|
|
|
|
| const response = await fetch(`${INTERNAL_BASE_URL}/api/sync/cloud`, {
|
| method: "POST",
|
| headers: { "Content-Type": "application/json" },
|
| body: JSON.stringify({ machineId: this.machineId, action: "sync" }),
|
| });
|
|
|
| if (!response.ok) {
|
| const errorData = await response.json().catch(() => ({}));
|
| throw new Error(errorData.error || "Sync failed");
|
| }
|
|
|
| const result = await response.json();
|
| return result;
|
| }
|
|
|
| |
| |
|
|
| isRunning() {
|
| return this.intervalId !== null;
|
| }
|
| }
|
|
|
|
|
| let cloudSyncScheduler: CloudSyncScheduler | null = null;
|
|
|
| export async function getCloudSyncScheduler(machineId: string | null = null, intervalMinutes = 15) {
|
| if (!cloudSyncScheduler) {
|
| cloudSyncScheduler = new CloudSyncScheduler(machineId, intervalMinutes);
|
| }
|
| return cloudSyncScheduler;
|
| }
|
|
|