clocktopus 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,12 @@
1
1
  import { Hono } from 'hono';
2
+ import { v4 as uuidv4 } from 'uuid';
2
3
  import { Clockify } from '../../clockify.js';
3
- import { completeLatestSession } from '../../lib/db.js';
4
+ import { completeLatestSession, getOpenSession, logSessionStart } from '../../lib/db.js';
5
+ import { stopJiraTimer } from '../../lib/jira.js';
6
+ function extractJiraTicket(description) {
7
+ const match = description.match(/\b([A-Z][A-Z0-9]+-\d+)\b/);
8
+ return match?.[1];
9
+ }
4
10
  const timerRoutes = new Hono();
5
11
  timerRoutes.get('/timer/active', async (c) => {
6
12
  try {
@@ -11,11 +17,19 @@ timerRoutes.get('/timer/active', async (c) => {
11
17
  const timer = await clockify.getActiveTimer(user.defaultWorkspace, user.id);
12
18
  if (!timer)
13
19
  return c.json({ active: false });
20
+ // Sync externally-started timers (e.g. from Clockify app or Jira plugin) to DB
21
+ const timerStart = timer.timeInterval.start;
22
+ const openSession = getOpenSession();
23
+ const alreadyTracked = openSession && openSession.startedAt.slice(0, 19) === timerStart.slice(0, 19);
24
+ if (!alreadyTracked) {
25
+ const jiraTicket = extractJiraTicket(timer.description ?? '');
26
+ logSessionStart(timer.id ?? uuidv4(), timer.projectId, timer.description ?? '', timerStart, jiraTicket);
27
+ }
14
28
  return c.json({
15
29
  active: true,
16
30
  description: timer.description,
17
31
  projectId: timer.projectId,
18
- start: timer.timeInterval.start,
32
+ start: timerStart,
19
33
  });
20
34
  }
21
35
  catch {
@@ -47,10 +61,23 @@ timerRoutes.post('/timer/stop', async (c) => {
47
61
  const user = await clockify.getUser();
48
62
  if (!user)
49
63
  return c.json({ ok: false, error: 'Could not connect to Clockify.' }, 500);
64
+ const openSession = getOpenSession();
50
65
  const result = await clockify.stopTimer(user.defaultWorkspace, user.id);
51
66
  if (!result)
52
67
  return c.json({ ok: false, error: 'Failed to stop timer.' }, 500);
53
- completeLatestSession(new Date().toISOString(), false);
68
+ const completedAt = new Date().toISOString();
69
+ completeLatestSession(completedAt, false);
70
+ if (openSession?.jiraTicket) {
71
+ const timeSpentSeconds = Math.round((new Date(completedAt).getTime() - new Date(openSession.startedAt).getTime()) / 1000);
72
+ if (timeSpentSeconds >= 60) {
73
+ try {
74
+ await stopJiraTimer(openSession.jiraTicket, timeSpentSeconds);
75
+ }
76
+ catch (err) {
77
+ console.error('Error stopping Jira timer:', err);
78
+ }
79
+ }
80
+ }
54
81
  return c.json({ ok: true });
55
82
  }
56
83
  catch {
package/dist/lib/db.js CHANGED
@@ -105,7 +105,7 @@ export function getLatestToken() {
105
105
  }
106
106
  export function logSessionStart(id, projectId, description, startedAt, jiraTicket) {
107
107
  const db = getDb();
108
- const stmt = db.prepare('INSERT INTO sessions (id, projectId, description, startedAt, isAutoCompleted, jiraTicket) VALUES (?, ?, ?, ?, ?, ?)');
108
+ const stmt = db.prepare('INSERT OR IGNORE INTO sessions (id, projectId, description, startedAt, isAutoCompleted, jiraTicket) VALUES (?, ?, ?, ?, ?, ?)');
109
109
  stmt.run(id, projectId, description, startedAt, 0, jiraTicket ?? null);
110
110
  }
111
111
  export function completeLatestSession(completedAt, isAutoCompleted = false) {
@@ -130,6 +130,14 @@ export function getSessionCount() {
130
130
  const row = stmt.get();
131
131
  return row.count;
132
132
  }
133
+ export function getOpenSession() {
134
+ const db = getDb();
135
+ const stmt = db.prepare('SELECT * FROM sessions WHERE completedAt IS NULL ORDER BY startedAt DESC LIMIT 1');
136
+ const row = stmt.get();
137
+ if (!row)
138
+ return null;
139
+ return SessionSchema.parse(row);
140
+ }
133
141
  export function getLatestSession() {
134
142
  const db = getDb();
135
143
  const stmt = db.prepare(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clocktopus",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {