diff --git a/src/__tests__/bot-commands.test.ts b/src/__tests__/bot-commands.test.ts index 82e9ad4..bec991e 100644 --- a/src/__tests__/bot-commands.test.ts +++ b/src/__tests__/bot-commands.test.ts @@ -45,7 +45,9 @@ describe("bot messages", () => { expect(message).toContain("https://example.com/preview/demo-app"); expect(message).not.toContain("Pairing Token:"); expect(message).toContain("1. Запусти bootstrap-команду локально:"); + expect(message).toContain("Эту команду можно запускать повторно для этого проекта."); expect(message).toContain("2. После bootstrap запусти:"); + expect(message).toContain("pnpm run dev"); }); it("includes TMA and preview links in launchMessage", () => { @@ -61,5 +63,6 @@ describe("bot messages", () => { expect(message).toContain("Telegram Link:"); expect(message).toContain("Preview URL:"); expect(message).toContain("Tunnel Status:"); + expect(message).toContain("pnpm run dev"); }); }); diff --git a/src/__tests__/projects.test.ts b/src/__tests__/projects.test.ts index d80f72d..2c97ccf 100644 --- a/src/__tests__/projects.test.ts +++ b/src/__tests__/projects.test.ts @@ -49,10 +49,9 @@ describe("createProject", () => { expect(result.project.title).toBe("Test App"); expect(result.project.status).toBe("draft"); expect(result.pairingToken.claimedAt).toBeNull(); + expect(result.pairingToken.expiresAt).toBeNull(); expect(result.state.projects).toHaveLength(1); expect(result.state.pairingTokens).toHaveLength(1); - expect(new Date(result.pairingToken.expiresAt).getTime() - new Date(result.pairingToken.createdAt).getTime()) - .toBe(30 * 60 * 1000); }); it("normalizes slug from title", () => { diff --git a/src/bot/commands.ts b/src/bot/commands.ts index cf79585..ef74f18 100644 --- a/src/bot/commands.ts +++ b/src/bot/commands.ts @@ -55,7 +55,7 @@ export function launchUsageMessage(): string { export function createdMessage( slug: string, bootstrapCmd: string, - pairingToken: string, + _pairingToken: string, tmaUrl: string, previewUrl: string, telegramMiniAppUrl: string, @@ -65,9 +65,10 @@ export function createdMessage( "", "1. Запусти bootstrap-команду локально:", `${esc(bootstrapCmd)}`, + "Эту команду можно запускать повторно для этого проекта.", "", "2. После bootstrap запусти:", - "npm run dev", + "pnpm run dev", "", "Ссылки:", `Preview URL: ${renderLink(previewUrl)}`, @@ -91,6 +92,6 @@ export function launchMessage( `Telegram Link: ${renderLink(telegramMiniAppUrl)}`, `Preview URL: ${renderLink(previewUrl)}`, "", - "Если preview офлайн, подними локально проект командой npm run dev.", + "Если preview офлайн, подними локально проект командой pnpm run dev.", ].join("\n"); } diff --git a/src/projects.ts b/src/projects.ts index f91a147..1b719e5 100644 --- a/src/projects.ts +++ b/src/projects.ts @@ -69,7 +69,7 @@ export function createProject( token: makeId("pair"), projectId, createdAt: now, - expiresAt: addMinutes(now, 30), + expiresAt: null, claimedAt: null, claimedDeviceId: null, }; @@ -97,7 +97,9 @@ export function inspectPairingToken( const project = state.projects.find((p) => p.id === tokenRecord.projectId) ?? null; if (!project) return { error: "ProjectNotFound" }; if (tokenRecord.claimedAt) return { error: "TokenAlreadyClaimed" }; - if (Date.parse(tokenRecord.expiresAt) <= Date.now()) return { error: "TokenExpired" }; + if (tokenRecord.expiresAt !== null && Date.parse(tokenRecord.expiresAt) <= Date.now()) { + return { error: "TokenExpired" }; + } return { project, @@ -135,7 +137,9 @@ export function claimPairingToken( }; } - if (Date.parse(tokenRecord.expiresAt) <= Date.now()) return { error: "TokenExpired" }; + if (tokenRecord.expiresAt !== null && Date.parse(tokenRecord.expiresAt) <= Date.now()) { + return { error: "TokenExpired" }; + } const deviceId = makeId("device"); const deviceSecret = makeId("secret"); diff --git a/src/types.ts b/src/types.ts index ccdd9c7..763b2ec 100644 --- a/src/types.ts +++ b/src/types.ts @@ -16,7 +16,7 @@ export interface PairingToken { token: string; projectId: string; createdAt: string; - expiresAt: string; + expiresAt: string | null; claimedAt: string | null; claimedDeviceId: string | null; }