diff --git a/api/src/controllers/admin.controller.ts b/api/src/controllers/admin.controller.ts index 154801e..62b6082 100644 --- a/api/src/controllers/admin.controller.ts +++ b/api/src/controllers/admin.controller.ts @@ -3,6 +3,7 @@ import fs from 'node:fs'; import path from 'node:path'; import process from 'node:process'; import { fileURLToPath } from 'node:url'; +import { createRequire } from 'node:module'; import { z } from 'zod'; import { AppDataSource } from '../config/data-source.js'; import { env } from '../config/env.js'; @@ -38,42 +39,53 @@ const userUpdateSchema = z.object({ defaultCurrency: z.string().min(3).max(8).optional() }); -type PackageJson = { version?: string }; - +const require = createRequire(import.meta.url); const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); -const readPackageJsonSafe = (filePath: string | null): PackageJson | null => { - if (!filePath || !fs.existsSync(filePath)) return null; +type PackageLike = { version?: string }; +function readPackageJsonSafe(filePath: string): PackageLike | null { try { - return JSON.parse(fs.readFileSync(filePath, 'utf8')) as PackageJson; + if (!fs.existsSync(filePath)) return null; + return JSON.parse(fs.readFileSync(filePath, 'utf8')) as PackageLike; } catch { return null; } -}; +} -const findNearestPackageJson = (startDir: string): string | null => { - let currentDir = path.resolve(startDir); +function findApiPackage(): PackageLike | null { + return ( + readPackageJsonSafe(path.resolve(__dirname, '../../package.json')) ?? + readPackageJsonSafe(path.resolve(__dirname, '../package.json')) + ); +} - while (true) { - const candidate = path.join(currentDir, 'package.json'); - if (fs.existsSync(candidate)) return candidate; - - const parentDir = path.dirname(currentDir); - if (parentDir == currentDir) return null; - currentDir = parentDir; +function findRootPackage(): PackageLike | null { + const candidates = [ + path.resolve(__dirname, '../../../package.json'), + path.resolve(__dirname, '../../package.json'), + path.resolve(__dirname, '../package.json') + ]; + for (const candidate of candidates) { + const pkg = readPackageJsonSafe(candidate); + if (!pkg) continue; + if (candidate.endsWith('/api/package.json') || candidate.endsWith('\\api\\package.json')) continue; + return pkg; } -}; + return null; +} -const apiPackagePath = findNearestPackageJson(__dirname); -const apiPackage = readPackageJsonSafe(apiPackagePath); -const apiRootDir = apiPackagePath ? path.dirname(apiPackagePath) : null; -const projectRootPackagePath = apiRootDir ? findNearestPackageJson(path.dirname(apiRootDir)) : null; -const rootPackage = readPackageJsonSafe(projectRootPackagePath); -const webPackage = readPackageJsonSafe( - projectRootPackagePath ? path.join(path.dirname(projectRootPackagePath), 'web', 'package.json') : null -); +function findWebPackage(): PackageLike | null { + return ( + readPackageJsonSafe(path.resolve(__dirname, '../../../web/package.json')) ?? + readPackageJsonSafe(path.resolve(__dirname, '../../web/package.json')) + ); +} + +const rootPackage = findRootPackage(); +const apiPackage = findApiPackage(); +const webPackage = findWebPackage(); const settingsRepo = () => AppDataSource.getRepository(AppSetting); const userRepo = () => AppDataSource.getRepository(User); diff --git a/api/src/entities/Budget.ts b/api/src/entities/Budget.ts index 1634b9c..177e3b8 100644 --- a/api/src/entities/Budget.ts +++ b/api/src/entities/Budget.ts @@ -1,4 +1,5 @@ import { Column, CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'; +import type { Relation } from 'typeorm'; import { Category } from './Category.js'; import { User } from './User.js'; import { decimalTransformer } from '../utils/decimal.js'; @@ -31,8 +32,8 @@ export class Budget { updatedAt!: Date; @ManyToOne(() => User, { onDelete: 'CASCADE' }) - user!: User; + user!: Relation; @ManyToOne(() => Category, { eager: true, nullable: true, onDelete: 'SET NULL' }) - category!: Category | null; + category!: Relation | null; } diff --git a/api/src/entities/Category.ts b/api/src/entities/Category.ts index 7160f9a..744682a 100644 --- a/api/src/entities/Category.ts +++ b/api/src/entities/Category.ts @@ -1,4 +1,5 @@ import { Column, CreateDateColumn, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from 'typeorm'; +import type { Relation } from 'typeorm'; import { Expense } from './Expense.js'; import { User } from './User.js'; import { dateTimeColumnType } from '../utils/db-column-types.js'; @@ -21,8 +22,8 @@ export class Category { createdAt!: Date; @ManyToOne(() => User, (user) => user.categories, { nullable: true, onDelete: 'CASCADE' }) - user!: User | null; + user!: Relation | null; @OneToMany(() => Expense, (expense) => expense.category) - expenses!: Expense[]; + expenses!: Relation; } diff --git a/api/src/entities/Expense.ts b/api/src/entities/Expense.ts index 23c856a..9d41c0f 100644 --- a/api/src/entities/Expense.ts +++ b/api/src/entities/Expense.ts @@ -1,4 +1,5 @@ import { Column, CreateDateColumn, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'; +import type { Relation } from 'typeorm'; import { Category } from './Category.js'; import { Proof } from './Proof.js'; import { User } from './User.js'; @@ -62,11 +63,11 @@ export class Expense { updatedAt!: Date; @ManyToOne(() => User, (user) => user.expenses, { onDelete: 'CASCADE' }) - user!: User; + user!: Relation; @ManyToOne(() => Category, (category) => category.expenses, { eager: true, onDelete: 'RESTRICT' }) - category!: Category; + category!: Relation; @OneToMany(() => Proof, (proof) => proof.expense, { eager: true, cascade: true }) - proofs!: Proof[]; + proofs!: Relation; } diff --git a/api/src/entities/Merchant.ts b/api/src/entities/Merchant.ts index 7714940..488501c 100644 --- a/api/src/entities/Merchant.ts +++ b/api/src/entities/Merchant.ts @@ -1,4 +1,5 @@ import { Column, CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'; +import type { Relation } from 'typeorm'; import { User } from './User.js'; import { dateTimeColumnType } from '../utils/db-column-types.js'; @@ -26,5 +27,5 @@ export class Merchant { updatedAt!: Date; @ManyToOne(() => User, { onDelete: 'CASCADE' }) - user!: User; + user!: Relation; } diff --git a/api/src/entities/Proof.ts b/api/src/entities/Proof.ts index 6d36398..4ea295a 100644 --- a/api/src/entities/Proof.ts +++ b/api/src/entities/Proof.ts @@ -1,4 +1,5 @@ import { Column, CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn } from 'typeorm'; +import type { Relation } from 'typeorm'; import { Expense } from './Expense.js'; import { dateTimeColumnType } from '../utils/db-column-types.js'; @@ -34,5 +35,5 @@ export class Proof { createdAt!: Date; @ManyToOne(() => Expense, (expense) => expense.proofs, { onDelete: 'CASCADE' }) - expense!: Expense; + expense!: Relation; } diff --git a/api/src/entities/RecurringExpense.ts b/api/src/entities/RecurringExpense.ts index 6aef3c2..cc2e16a 100644 --- a/api/src/entities/RecurringExpense.ts +++ b/api/src/entities/RecurringExpense.ts @@ -1,4 +1,5 @@ import { Column, CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'; +import type { Relation } from 'typeorm'; import { Category } from './Category.js'; import { User } from './User.js'; import { decimalTransformer } from '../utils/decimal.js'; @@ -72,8 +73,8 @@ export class RecurringExpense { updatedAt!: Date; @ManyToOne(() => User, { onDelete: 'CASCADE' }) - user!: User; + user!: Relation; @ManyToOne(() => Category, { eager: true, onDelete: 'RESTRICT' }) - category!: Category; + category!: Relation; } diff --git a/api/src/entities/User.ts b/api/src/entities/User.ts index f48d327..4a4b983 100644 --- a/api/src/entities/User.ts +++ b/api/src/entities/User.ts @@ -1,4 +1,5 @@ import { Column, CreateDateColumn, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm'; +import type { Relation } from 'typeorm'; import { Category } from './Category.js'; import { Expense } from './Expense.js'; import { dateTimeColumnType } from '../utils/db-column-types.js'; @@ -51,8 +52,8 @@ export class User { createdAt!: Date; @OneToMany(() => Category, (category) => category.user) - categories!: Category[]; + categories!: Relation; @OneToMany(() => Expense, (expense) => expense.user) - expenses!: Expense[]; + expenses!: Relation; } diff --git a/api/src/utils/db-column-types.ts b/api/src/utils/db-column-types.ts index eb67ec2..9cd95ba 100644 --- a/api/src/utils/db-column-types.ts +++ b/api/src/utils/db-column-types.ts @@ -1,5 +1,2 @@ -import type { ColumnType } from 'typeorm'; - -const dbType = (process.env.DB_TYPE ?? 'sqlite').toLowerCase(); - -export const dateTimeColumnType: ColumnType = dbType === 'postgres' ? 'timestamp' : 'datetime'; +export const dateTimeColumnType: 'timestamp' | 'datetime' = + process.env.DB_TYPE === 'postgres' ? 'timestamp' : 'datetime'; diff --git a/web/angular.json b/web/angular.json index a3ecd01..49c8113 100644 --- a/web/angular.json +++ b/web/angular.json @@ -34,7 +34,7 @@ "budgets": [ { "type": "initial", - "maximumWarning": "1MB", + "maximumWarning": "1.5MB", "maximumError": "2MB" } ],