Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
<p align="center">
<img src="https://img.shields.io/badge/Angular-21-DD0031?logo=angular" alt="Angular 21">
<img src="https://img.shields.io/badge/Electron-37-47848F?logo=electron" alt="Electron 37">
<img src="https://img.shields.io/badge/Prisma-6-2D3748?logo=prisma" alt="Prisma">
<img src="https://img.shields.io/badge/Prisma-7-2D3748?logo=prisma" alt="Prisma">
<img src="https://img.shields.io/badge/SQLite-3-003B57?logo=sqlite" alt="SQLite">
<img src="https://img.shields.io/badge/License-GPL%20v3-blue.svg" alt="License: GPL v3">
</p>
Expand All @@ -55,9 +55,15 @@

## 📥 Installation

### Windows
### Download

Download the latest installer from the [Releases](https://github.com/altaskur/OpenTimeTracker/releases) page.
Download the latest installer for your platform from the [Releases](https://github.com/altaskur/OpenTimeTracker/releases) page:

| Platform | Format | Architecture |
| -------- | ----------------------- | -------------------------- |
| Windows | `.exe` (NSIS installer) | x64 |
| macOS | `.dmg` | x64, arm64 (Apple Silicon) |
| Linux | `.AppImage`, `.deb` | x64 |

### Build from Source

Expand Down Expand Up @@ -90,7 +96,10 @@ npm run dev
| `npm start` | Start Angular dev server (port 4200) |
| `npm run dev` | Build and run Electron app |
| `npm run build` | Production build |
| `npm run dist` | Generate installer for current OS |
| `npm run dist:win` | Generate Windows installer |
| `npm run dist:mac` | Generate macOS installer (DMG) |
| `npm run dist:linux` | Generate Linux installers |
| `npm test` | Run Angular tests |
| `npm run test:electron` | Run Electron tests |
| `npm run lint` | Run ESLint |
Expand Down
38 changes: 27 additions & 11 deletions electron/src/utils/paths.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { describe, it, expect, beforeEach, vi, type Mock, type Mocked } from 'vitest';
import {
describe,
it,
expect,
beforeEach,
afterEach,
vi,
type Mock,
type Mocked,
} from 'vitest';
import path from 'node:path';
import { app } from 'electron';
import {
Expand All @@ -17,9 +26,25 @@ import {
*/
describe('Paths Utility', () => {
const mockApp = app as Mocked<typeof app>;
const originalResourcesPath = process.resourcesPath;

beforeEach(() => {
vi.clearAllMocks();
// Mock process.resourcesPath for packaged mode tests
Object.defineProperty(process, 'resourcesPath', {
value: '/mock/app/resources',
configurable: true,
writable: true,
});
});

afterEach(() => {
// Restore original value
Object.defineProperty(process, 'resourcesPath', {
value: originalResourcesPath,
configurable: true,
writable: true,
});
});

describe('isPackaged', () => {
Expand Down Expand Up @@ -150,9 +175,6 @@ describe('Paths Utility', () => {
value: true,
configurable: true,
});
(mockApp.getPath as Mock).mockReturnValue(
'C:\\Program Files\\OpenTimeTracker\\OpenTimeTracker.exe',
);

const result = getIndexPath();

Expand Down Expand Up @@ -180,9 +202,6 @@ describe('Paths Utility', () => {
value: true,
configurable: true,
});
(mockApp.getPath as Mock).mockReturnValue(
'C:\\Program Files\\OpenTimeTracker\\OpenTimeTracker.exe',
);

const result = getPreloadPath();

Expand Down Expand Up @@ -210,14 +229,11 @@ describe('Paths Utility', () => {
value: true,
configurable: true,
});
(mockApp.getPath as Mock).mockReturnValue(
'C:\\Program Files\\OpenTimeTracker\\OpenTimeTracker.exe',
);

const result = getTemplateDatabasePath();

expect(result).toContain('resources');
expect(result).toContain('app.asar');
expect(result).toContain('app.asar.unpacked');
expect(result).toContain('template.db');
});
});
Expand Down
16 changes: 8 additions & 8 deletions electron/src/utils/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,12 @@ export const getBackupPath = (): string => {
* Gets the path to the Angular index.html file.
* In development: dist/OpenTimeTracker/browser/index.html
* In production: resources/app.asar/dist/OpenTimeTracker/browser/index.html
* Note: Uses process.resourcesPath for cross-platform compatibility (Windows/macOS/Linux)
*/
export const getIndexPath = (): string => {
if (isPackaged()) {
return path.join(
path.dirname(app.getPath('exe')),
'resources',
process.resourcesPath,
'app.asar',
'dist',
'OpenTimeTracker',
Expand All @@ -80,12 +80,12 @@ export const getIndexPath = (): string => {

/**
* Gets the path to the preload script.
* Note: Uses process.resourcesPath for cross-platform compatibility (Windows/macOS/Linux)
*/
export const getPreloadPath = (): string => {
if (isPackaged()) {
return path.join(
path.dirname(app.getPath('exe')),
'resources',
process.resourcesPath,
'app.asar',
'dist',
'electron',
Expand All @@ -100,14 +100,14 @@ export const getPreloadPath = (): string => {
* Gets the path to the template database file.
* Used to initialize a new database with pre-created schema.
* In development: prisma/template.db
* In production: resources/app.asar/prisma/template.db
* In production: resources/app.asar.unpacked/prisma/template.db
* Note: Template is in asarUnpack so we use app.asar.unpacked for cross-platform compatibility
*/
export const getTemplateDatabasePath = (): string => {
if (isPackaged()) {
return path.join(
path.dirname(app.getPath('exe')),
'resources',
'app.asar',
process.resourcesPath,
'app.asar.unpacked',
'prisma',
'template.db',
);
Expand Down
34 changes: 32 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@
"dist": "npm run build && electron-builder",
"dist:win": "npm run build && electron-builder --win",
"dist:linux": "npm run build && electron-builder --linux",
"dist:mac": "npm run build && electron-builder --mac",
"pack": "electron-builder --dir",
"prisma:generate": "prisma generate",
"prisma:generate": "cross-env DATABASE_URL=file:./dist/data/timetracker.db prisma generate",
"prisma:push": "cross-env DATABASE_URL=file:./dist/data/timetracker.db prisma db push",
"prisma:studio": "cross-env DATABASE_URL=file:./dist/data/timetracker.db prisma studio",
"prisma:migrate": "cross-env DATABASE_URL=file:./dist/data/timetracker.db prisma migrate dev",
Expand Down Expand Up @@ -57,7 +58,8 @@
"node_modules/@prisma/client/**/*",
"node_modules/@prisma/adapter-better-sqlite3/**/*",
"node_modules/better-sqlite3/**/*",
"dist/electron/generated/**/*"
"dist/electron/generated/**/*",
"prisma/template.db"
],
"win": {
"target": [
Expand Down Expand Up @@ -99,6 +101,34 @@
"maintainer": "altaskur",
"synopsis": "Time tracking application",
"description": "Free and open source time tracking application for developers and teams"
},
"mac": {
"target": [
{
"target": "dmg",
"arch": [
"x64",
"arm64"
]
}
],
"icon": "public/icon-512.png",
"category": "public.app-category.productivity"
},
"dmg": {
"title": "${productName} ${version}",
"contents": [
{
"x": 130,
"y": 220
},
{
"x": 410,
"y": 220,
"type": "link",
"path": "/Applications"
}
]
}
},
"prettier": {
Expand Down
6 changes: 4 additions & 2 deletions prisma.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import path from 'node:path';
import { defineConfig } from 'prisma/config';
import { defineConfig, env } from 'prisma/config';

export default defineConfig({
earlyAccess: true,
schema: path.join(import.meta.dirname, 'prisma', 'schema.prisma'),
datasource: {
url: env('DATABASE_URL') ?? 'file:./dist/data/timetracker.db',
},
});
Binary file modified prisma/template.db
Binary file not shown.
Binary file added public/icon-512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions scripts/update-db-template.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ const templatePath = join(__dirname, "..", "prisma", "template.db");
console.log("Regenerating database template...\n");

try {
console.log("1. Resetting database with migrations...");
console.log("1. Resetting database with schema...");
execSync(
"cross-env DATABASE_URL=file:./dist/data/timetracker.db npx prisma migrate reset --force --skip-seed --skip-generate",
"cross-env DATABASE_URL=file:./dist/data/timetracker.db npx prisma db push --force-reset --accept-data-loss",
{
stdio: "inherit",
cwd: join(__dirname, ".."),
Expand Down