Skip to content

Commit 4dc79aa

Browse files
committed
fix: limit hackatime projects to oct 10
1 parent f9dfe11 commit 4dc79aa

File tree

4 files changed

+86
-105
lines changed

4 files changed

+86
-105
lines changed

lark-ui/src/routes/app/projects/[id]/+layout.server.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ export const load: LayoutServerLoad = async ({ params, fetch }) => {
99
throw redirect(302, '/');
1010
}
1111

12+
if (!/^\d+$/.test(params.id)) {
13+
throw new Error('Invalid project ID');
14+
}
15+
1216
const project = await getProject(params.id, fetch);
1317

1418
if (!project) {

owl-api/src/admin/admin.service.ts

Lines changed: 27 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -695,8 +695,11 @@ export class AdminService {
695695
projectNames: string[],
696696
baseUrl: string,
697697
apiKey?: string,
698-
cutoffDate: Date = new Date('2024-10-10T00:00:00Z'),
698+
cutoffDate: Date = new Date('2025-10-10T00:00:00Z'),
699699
): Promise<Map<string, number>> {
700+
const startDate = cutoffDate.toISOString().split('T')[0];
701+
const uri = `https://hackatime.hackclub.com/api/v1/users/${hackatimeAccount}/stats?features=projects&start_date=${startDate}`;
702+
700703
const headers: Record<string, string> = {
701704
'Content-Type': 'application/json',
702705
};
@@ -705,47 +708,36 @@ export class AdminService {
705708
headers['Authorization'] = `Bearer ${apiKey}`;
706709
}
707710

708-
const cutoffTimestamp = Math.floor(cutoffDate.getTime() / 1000);
709711
const durationsMap = new Map<string, number>();
710712

711713
for (const projectName of projectNames) {
712-
const sanitizedProjectName = projectName.replace(/'/g, "''");
713-
const query = {
714-
query: `
715-
SELECT
716-
COALESCE(SUM(duration), 0) as total_duration
717-
FROM
718-
time_entries
719-
WHERE
720-
user_id = ${hackatimeAccount}
721-
AND project_name = '${sanitizedProjectName}'
722-
AND time >= ${cutoffTimestamp}
723-
`,
724-
};
714+
durationsMap.set(projectName, 0);
715+
}
725716

726-
try {
727-
const response = await fetch(`${baseUrl}/execute`, {
728-
method: 'POST',
729-
headers,
730-
body: JSON.stringify(query),
731-
});
717+
try {
718+
const response = await fetch(uri, {
719+
method: 'GET',
720+
headers,
721+
});
732722

733-
if (response.ok) {
734-
const data = await response.json();
735-
if (data.rows && data.rows.length > 0) {
736-
const duration = typeof data.rows[0].total_duration === 'number'
737-
? data.rows[0].total_duration
738-
: 0;
739-
durationsMap.set(projectName, duration);
740-
} else {
741-
durationsMap.set(projectName, 0);
723+
if (response.ok) {
724+
const responseData = await response.json();
725+
const projects = responseData?.data?.projects;
726+
727+
if (projects && Array.isArray(projects)) {
728+
for (const project of projects) {
729+
const name = project?.name;
730+
if (typeof name === 'string' && projectNames.includes(name)) {
731+
const duration = typeof project?.total_seconds === 'number'
732+
? project.total_seconds
733+
: 0;
734+
durationsMap.set(name, duration);
735+
}
742736
}
743-
} else {
744-
durationsMap.set(projectName, 0);
745737
}
746-
} catch (error) {
747-
durationsMap.set(projectName, 0);
748738
}
739+
} catch (error) {
740+
console.error('Error fetching hackatime stats:', error);
749741
}
750742

751743
return durationsMap;
@@ -759,7 +751,7 @@ export class AdminService {
759751
apiKey?: string,
760752
) {
761753
if (hackatimeAccount && baseUrl) {
762-
const cutoffDate = new Date('2024-10-10T00:00:00Z');
754+
const cutoffDate = new Date('2025-10-10T00:00:00Z');
763755
const filteredDurations = await this.fetchHackatimeProjectDurationsAfterDate(
764756
hackatimeAccount,
765757
projectNames,

owl-api/src/projects/projects.service.ts

Lines changed: 28 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -539,8 +539,12 @@ export class ProjectsService {
539539
projectNames: string[],
540540
baseUrl: string,
541541
apiKey?: string,
542-
cutoffDate: Date = new Date('2024-10-10T00:00:00Z'),
542+
cutoffDate: Date = new Date('2025-10-10T00:00:00Z'),
543543
): Promise<Map<string, number>> {
544+
const startDate = cutoffDate.toISOString().split('T')[0];
545+
const hackatimeApiUrl = baseUrl.replace('/api/admin/v1', '/api/v1');
546+
const uri = `https://hackatime.hackclub.com/api/v1/users/${hackatimeAccount}/stats?features=projects&start_date=${startDate}`;
547+
544548
const headers: Record<string, string> = {
545549
'Content-Type': 'application/json',
546550
};
@@ -549,47 +553,36 @@ export class ProjectsService {
549553
headers['Authorization'] = `Bearer ${apiKey}`;
550554
}
551555

552-
const cutoffTimestamp = Math.floor(cutoffDate.getTime() / 1000);
553556
const durationsMap = new Map<string, number>();
554557

555558
for (const projectName of projectNames) {
556-
const sanitizedProjectName = projectName.replace(/'/g, "''");
557-
const query = {
558-
query: `
559-
SELECT
560-
COALESCE(SUM(duration), 0) as total_duration
561-
FROM
562-
time_entries
563-
WHERE
564-
user_id = ${hackatimeAccount}
565-
AND project_name = '${sanitizedProjectName}'
566-
AND time >= ${cutoffTimestamp}
567-
`,
568-
};
559+
durationsMap.set(projectName, 0);
560+
}
569561

570-
try {
571-
const response = await fetch(`${baseUrl}/execute`, {
572-
method: 'POST',
573-
headers,
574-
body: JSON.stringify(query),
575-
});
562+
try {
563+
const response = await fetch(uri, {
564+
method: 'GET',
565+
headers,
566+
});
576567

577-
if (response.ok) {
578-
const data = await response.json();
579-
if (data.rows && data.rows.length > 0) {
580-
const duration = typeof data.rows[0].total_duration === 'number'
581-
? data.rows[0].total_duration
582-
: 0;
583-
durationsMap.set(projectName, duration);
584-
} else {
585-
durationsMap.set(projectName, 0);
568+
if (response.ok) {
569+
const responseData = await response.json();
570+
const projects = responseData?.data?.projects;
571+
572+
if (projects && Array.isArray(projects)) {
573+
for (const project of projects) {
574+
const name = project?.name;
575+
if (typeof name === 'string' && projectNames.includes(name)) {
576+
const duration = typeof project?.total_seconds === 'number'
577+
? project.total_seconds
578+
: 0;
579+
durationsMap.set(name, duration);
580+
}
586581
}
587-
} else {
588-
durationsMap.set(projectName, 0);
589582
}
590-
} catch (error) {
591-
durationsMap.set(projectName, 0);
592583
}
584+
} catch (error) {
585+
console.error('Error fetching hackatime stats:', error);
593586
}
594587

595588
return durationsMap;
@@ -603,7 +596,7 @@ export class ProjectsService {
603596
apiKey?: string,
604597
) {
605598
if (hackatimeAccount && baseUrl) {
606-
const cutoffDate = new Date('2024-10-10T00:00:00Z');
599+
const cutoffDate = new Date('2025-10-10T00:00:00Z');
607600
const filteredDurations = await this.fetchHackatimeProjectDurationsAfterDate(
608601
hackatimeAccount,
609602
projectNames,

owl-api/src/user/user.service.ts

Lines changed: 27 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -803,8 +803,11 @@ export class UserService {
803803
projectNames: string[],
804804
baseUrl: string,
805805
apiKey?: string,
806-
cutoffDate: Date = new Date('2024-10-10T00:00:00Z'),
806+
cutoffDate: Date = new Date('2025-10-10T00:00:00Z'),
807807
): Promise<Map<string, number>> {
808+
const startDate = cutoffDate.toISOString().split('T')[0];
809+
const uri = `https://hackatime.hackclub.com/api/v1/users/${hackatimeAccount}/stats?features=projects&start_date=${startDate}`;
810+
808811
const headers: Record<string, string> = {
809812
'Content-Type': 'application/json',
810813
};
@@ -813,47 +816,36 @@ export class UserService {
813816
headers['Authorization'] = `Bearer ${apiKey}`;
814817
}
815818

816-
const cutoffTimestamp = Math.floor(cutoffDate.getTime() / 1000);
817819
const durationsMap = new Map<string, number>();
818820

819821
for (const projectName of projectNames) {
820-
const sanitizedProjectName = projectName.replace(/'/g, "''");
821-
const query = {
822-
query: `
823-
SELECT
824-
COALESCE(SUM(duration), 0) as total_duration
825-
FROM
826-
time_entries
827-
WHERE
828-
user_id = ${hackatimeAccount}
829-
AND project_name = '${sanitizedProjectName}'
830-
AND time >= ${cutoffTimestamp}
831-
`,
832-
};
822+
durationsMap.set(projectName, 0);
823+
}
833824

834-
try {
835-
const response = await fetch(`${baseUrl}/execute`, {
836-
method: 'POST',
837-
headers,
838-
body: JSON.stringify(query),
839-
});
825+
try {
826+
const response = await fetch(uri, {
827+
method: 'GET',
828+
headers,
829+
});
840830

841-
if (response.ok) {
842-
const data = await response.json();
843-
if (data.rows && data.rows.length > 0) {
844-
const duration = typeof data.rows[0].total_duration === 'number'
845-
? data.rows[0].total_duration
846-
: 0;
847-
durationsMap.set(projectName, duration);
848-
} else {
849-
durationsMap.set(projectName, 0);
831+
if (response.ok) {
832+
const responseData = await response.json();
833+
const projects = responseData?.data?.projects;
834+
835+
if (projects && Array.isArray(projects)) {
836+
for (const project of projects) {
837+
const name = project?.name;
838+
if (typeof name === 'string' && projectNames.includes(name)) {
839+
const duration = typeof project?.total_seconds === 'number'
840+
? project.total_seconds
841+
: 0;
842+
durationsMap.set(name, duration);
843+
}
850844
}
851-
} else {
852-
durationsMap.set(projectName, 0);
853845
}
854-
} catch (error) {
855-
durationsMap.set(projectName, 0);
856846
}
847+
} catch (error) {
848+
console.error('Error fetching hackatime stats:', error);
857849
}
858850

859851
return durationsMap;
@@ -867,7 +859,7 @@ export class UserService {
867859
apiKey?: string,
868860
) {
869861
if (hackatimeAccount && baseUrl) {
870-
const cutoffDate = new Date('2024-10-10T00:00:00Z');
862+
const cutoffDate = new Date('2025-10-10T00:00:00Z');
871863
const filteredDurations = await this.fetchHackatimeProjectDurationsAfterDate(
872864
hackatimeAccount,
873865
projectNames,

0 commit comments

Comments
 (0)