poprawki
This commit is contained in:
@@ -10,7 +10,6 @@ DB_USER=expense_app
|
||||
DB_PASSWORD=expense_app
|
||||
DB_SYNC=true
|
||||
DB_LOGGING=false
|
||||
APP_NAME=Expense Control
|
||||
DEFAULT_CURRENCY=PLN
|
||||
ADMIN_EMAIL=admin@example.com
|
||||
ADMIN_PASSWORD=ChangeMe123!
|
||||
|
||||
39
api/package-lock.json
generated
39
api/package-lock.json
generated
@@ -604,9 +604,6 @@
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -621,9 +618,6 @@
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -638,9 +632,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -655,9 +646,6 @@
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -672,9 +660,6 @@
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -689,9 +674,6 @@
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -706,9 +688,6 @@
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -723,9 +702,6 @@
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -740,9 +716,6 @@
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -757,9 +730,6 @@
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -774,9 +744,6 @@
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -791,9 +758,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
@@ -808,9 +772,6 @@
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
|
||||
@@ -23,7 +23,6 @@ export const env = {
|
||||
DB_PATH: process.env.DB_PATH ?? './data/dev.sqlite',
|
||||
DB_SYNC: toBoolean(process.env.DB_SYNC, true),
|
||||
DB_LOGGING: toBoolean(process.env.DB_LOGGING, false),
|
||||
APP_NAME: process.env.APP_NAME ?? 'Expense Control',
|
||||
DEFAULT_CURRENCY: process.env.DEFAULT_CURRENCY ?? 'PLN',
|
||||
ADMIN_EMAIL: process.env.ADMIN_EMAIL ?? 'admin@example.com',
|
||||
ADMIN_PASSWORD: process.env.ADMIN_PASSWORD ?? 'Admin123!',
|
||||
|
||||
@@ -23,6 +23,7 @@ const loginSchema = z.object({
|
||||
password: z.string().min(8).max(100)
|
||||
});
|
||||
|
||||
const DEFAULT_APP_NAME = 'Expense Control';
|
||||
|
||||
export const publicConfig = async (_req: Request, res: Response) => {
|
||||
const settings = await AppDataSource.getRepository(AppSetting).find({
|
||||
@@ -31,7 +32,7 @@ export const publicConfig = async (_req: Request, res: Response) => {
|
||||
});
|
||||
|
||||
return res.json({
|
||||
appName: settings[0]?.appName ?? 'Expense Control',
|
||||
appName: settings[0]?.appName ?? DEFAULT_APP_NAME,
|
||||
registrationEnabled: settings[0]?.registrationEnabled ?? true
|
||||
});
|
||||
};
|
||||
|
||||
@@ -36,7 +36,7 @@ export const listMerchants = async (req: AuthenticatedRequest, res: Response) =>
|
||||
export const createMerchant = async (req: AuthenticatedRequest, res: Response) => {
|
||||
const parsed = schema.safeParse(req.body);
|
||||
if (!parsed.success) {
|
||||
return res.status(400).json({ message: 'Invalid partner payload', issues: parsed.error.issues });
|
||||
return res.status(400).json({ message: 'Invalid merchant payload', issues: parsed.error.issues });
|
||||
}
|
||||
|
||||
const user = await userRepo().findOneOrFail({ where: { id: req.user!.id } });
|
||||
@@ -47,13 +47,13 @@ export const createMerchant = async (req: AuthenticatedRequest, res: Response) =
|
||||
export const updateMerchant = async (req: AuthenticatedRequest, res: Response) => {
|
||||
const parsed = schema.safeParse(req.body);
|
||||
if (!parsed.success) {
|
||||
return res.status(400).json({ message: 'Invalid partner payload', issues: parsed.error.issues });
|
||||
return res.status(400).json({ message: 'Invalid merchant payload', issues: parsed.error.issues });
|
||||
}
|
||||
|
||||
const item = await merchantRepo().findOne({
|
||||
where: { id: String(req.params.id), user: { id: req.user!.id } }
|
||||
});
|
||||
if (!item) return res.status(404).json({ message: 'Partner not found' });
|
||||
if (!item) return res.status(404).json({ message: 'Merchant not found' });
|
||||
|
||||
item.name = parsed.data.name;
|
||||
item.kind = parsed.data.kind;
|
||||
@@ -68,7 +68,7 @@ export const deleteMerchant = async (req: AuthenticatedRequest, res: Response) =
|
||||
const item = await merchantRepo().findOne({
|
||||
where: { id: String(req.params.id), user: { id: req.user!.id } }
|
||||
});
|
||||
if (!item) return res.status(404).json({ message: 'Partner not found' });
|
||||
if (!item) return res.status(404).json({ message: 'Merchant not found' });
|
||||
|
||||
await merchantRepo().remove(item);
|
||||
return res.status(204).send();
|
||||
|
||||
@@ -29,16 +29,23 @@ const defaultPrefs = (email: string) => ({
|
||||
categoryIds: [] as string[]
|
||||
});
|
||||
|
||||
const formatLocalDate = (date: Date) => {
|
||||
const year = date.getFullYear();
|
||||
const month = `${date.getMonth() + 1}`.padStart(2, '0');
|
||||
const day = `${date.getDate()}`.padStart(2, '0');
|
||||
return `${year}-${month}-${day}`;
|
||||
};
|
||||
|
||||
const periodRange = (frequency: 'monthly' | 'yearly' | 'threshold') => {
|
||||
const now = new Date();
|
||||
const endDate = now.toISOString().slice(0, 10);
|
||||
const endDate = formatLocalDate(now);
|
||||
|
||||
if (frequency === 'yearly') {
|
||||
return { startDate: `${now.getUTCFullYear()}-01-01`, endDate, bucket: 'month' as const, label: 'Year to date' };
|
||||
return { startDate: `${now.getFullYear()}-01-01`, endDate, bucket: 'month' as const, label: 'Year to date' };
|
||||
}
|
||||
|
||||
const start = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1));
|
||||
return { startDate: start.toISOString().slice(0, 10), endDate, bucket: 'month' as const, label: 'Current month' };
|
||||
const start = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||
return { startDate: formatLocalDate(start), endDate, bucket: 'month' as const, label: 'Current month' };
|
||||
};
|
||||
|
||||
const buildReportHtml = (title: string, summary: Awaited<ReturnType<typeof getStatistics>>) => {
|
||||
|
||||
@@ -4,15 +4,15 @@ import { env } from '../config/env.js';
|
||||
import type { AuthenticatedRequest } from '../types/express.js';
|
||||
export const requireAuth = (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
|
||||
const header = req.headers.authorization;
|
||||
if (!header?.startsWith('Bearer ')) return res.status(401).json({ message: 'Brak tokenu autoryzacji' });
|
||||
if (!header?.startsWith('Bearer ')) return res.status(401).json({ message: 'Authorization token is missing' });
|
||||
try {
|
||||
req.user = jwt.verify(header.replace('Bearer ', ''), env.JWT_SECRET) as { id: string; email: string; role: 'ADMIN' | 'USER' };
|
||||
return next();
|
||||
} catch {
|
||||
return res.status(401).json({ message: 'Nieprawidlowy token' });
|
||||
return res.status(401).json({ message: 'Invalid token' });
|
||||
}
|
||||
};
|
||||
export const requireAdmin = (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
|
||||
if (!req.user || req.user.role !== 'ADMIN') return res.status(403).json({ message: 'Wymagane uprawnienia administratora' });
|
||||
if (!req.user || req.user.role !== 'ADMIN') return res.status(403).json({ message: 'Administrator access is required' });
|
||||
return next();
|
||||
};
|
||||
|
||||
@@ -39,7 +39,7 @@ export const createUser = async (input: {
|
||||
defaultCurrency?: string;
|
||||
}) => {
|
||||
const existing = await repo().findOne({ where: { email: input.email.toLowerCase() } });
|
||||
if (existing) throw new Error('Email already exists');
|
||||
if (existing) throw new Error('Email address is already in use');
|
||||
|
||||
const user = repo().create({
|
||||
fullName: input.fullName,
|
||||
|
||||
@@ -4,6 +4,8 @@ import { AppSetting } from '../entities/AppSetting.js';
|
||||
import { Category } from '../entities/Category.js';
|
||||
import { createUser, findUserByEmail } from './auth.service.js';
|
||||
|
||||
const DEFAULT_APP_NAME = 'Expense Control';
|
||||
|
||||
const systemCategories = [
|
||||
{ name: 'Rachunki', color: '#b91c1c' },
|
||||
{ name: 'Zakupy', color: '#2563eb' },
|
||||
@@ -41,7 +43,7 @@ export const bootstrapData = async () => {
|
||||
if (!settings) {
|
||||
await settingsRepo.save(
|
||||
settingsRepo.create({
|
||||
appName: env.APP_NAME,
|
||||
appName: DEFAULT_APP_NAME,
|
||||
defaultCurrency: env.DEFAULT_CURRENCY,
|
||||
registrationEnabled: true,
|
||||
allowedProofTypes: ['RECEIPT', 'INVOICE', 'NOTE', 'BANK_STATEMENT', 'OTHER'],
|
||||
@@ -52,7 +54,7 @@ export const bootstrapData = async () => {
|
||||
smtpSecure: false,
|
||||
smtpUser: null,
|
||||
smtpPassword: null,
|
||||
smtpFromName: env.APP_NAME,
|
||||
smtpFromName: DEFAULT_APP_NAME,
|
||||
smtpFromEmail: env.ADMIN_EMAIL
|
||||
})
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user