style: run prettier on entire codebase

This commit is contained in:
BOHA
2026-03-24 19:59:14 +01:00
parent 872be42107
commit 3c167cf5c4
148 changed files with 26740 additions and 13990 deletions

View File

@@ -21,7 +21,7 @@ function getEasterSunday(year: number): string {
const m = Math.floor((a + 11 * h + 22 * l) / 451);
const month = Math.floor((h + l - 7 * m + 114) / 31);
const day = ((h + l - 7 * m + 114) % 31) + 1;
return `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
}
/** All Czech public holidays for a year (11 fixed + 2 Easter-based) */
@@ -51,8 +51,9 @@ export function getHolidays(year: number): string[] {
const easterMonday = new Date(easterDate);
easterMonday.setDate(easterMonday.getDate() + 1);
const fmt = (d: Date) => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
holidays.push(fmt(goodFriday)); // Velký pátek
const fmt = (d: Date) =>
`${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
holidays.push(fmt(goodFriday)); // Velký pátek
holidays.push(fmt(easterMonday)); // Velikonoční pondělí
holidays.sort();
@@ -67,7 +68,11 @@ export function isHoliday(dateStr: string): boolean {
}
/** Business days in a month (Mon-Fri excluding public holidays) */
export function getBusinessDaysInMonth(year: number, month: number, upToDay?: number): number {
export function getBusinessDaysInMonth(
year: number,
month: number,
upToDay?: number,
): number {
const holidays = getHolidays(year);
let count = 0;
const daysInMonth = new Date(year, month + 1, 0).getDate();
@@ -77,7 +82,7 @@ export function getBusinessDaysInMonth(year: number, month: number, upToDay?: nu
const date = new Date(year, month, day);
const dow = date.getDay();
if (dow !== 0 && dow !== 6) {
const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
const dateStr = `${year}-${String(month + 1).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
if (!holidays.includes(dateStr)) {
count++;
}
@@ -87,6 +92,10 @@ export function getBusinessDaysInMonth(year: number, month: number, upToDay?: nu
}
/** Monthly work fund in hours (business days × 8) */
export function getMonthlyWorkFund(year: number, month: number, upToDay?: number): number {
export function getMonthlyWorkFund(
year: number,
month: number,
upToDay?: number,
): number {
return getBusinessDaysInMonth(year, month, upToDay) * 8;
}

View File

@@ -1,45 +1,48 @@
import crypto from 'crypto';
import { config } from '../config/env';
import crypto from "crypto";
import { config } from "../config/env";
const ALGORITHM = 'aes-256-gcm';
const ALGORITHM = "aes-256-gcm";
const IV_LENGTH = 12;
const TAG_LENGTH = 16;
export function encrypt(plaintext: string): string {
const key = Buffer.from(config.totp.encryptionKey, 'hex');
const key = Buffer.from(config.totp.encryptionKey, "hex");
const iv = crypto.randomBytes(IV_LENGTH);
const cipher = crypto.createCipheriv(ALGORITHM, key, iv);
const encrypted = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]);
const encrypted = Buffer.concat([
cipher.update(plaintext, "utf8"),
cipher.final(),
]);
const tag = cipher.getAuthTag();
// Use PHP-compatible format: base64(nonce + ciphertext + tag)
return Buffer.concat([iv, encrypted, tag]).toString('base64');
return Buffer.concat([iv, encrypted, tag]).toString("base64");
}
export function decrypt(ciphertext: string): string {
const key = Buffer.from(config.totp.encryptionKey, 'hex');
const key = Buffer.from(config.totp.encryptionKey, "hex");
// Detect format: PHP uses base64(nonce+ciphertext+tag), TS uses hex:hex:hex
const parts = ciphertext.split(':');
const parts = ciphertext.split(":");
if (parts.length === 3) {
// TS format: iv:encrypted:tag (hex)
const iv = Buffer.from(parts[0], 'hex');
const iv = Buffer.from(parts[0], "hex");
const encrypted = parts[1];
const tag = Buffer.from(parts[2], 'hex');
const tag = Buffer.from(parts[2], "hex");
const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
decipher.setAuthTag(tag);
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
let decrypted = decipher.update(encrypted, "hex", "utf8");
decrypted += decipher.final("utf8");
return decrypted;
}
// PHP format: base64(nonce + ciphertext + tag)
const raw = Buffer.from(ciphertext, 'base64');
const raw = Buffer.from(ciphertext, "base64");
if (raw.length < IV_LENGTH + TAG_LENGTH + 1) {
throw new Error('Invalid ciphertext format');
throw new Error("Invalid ciphertext format");
}
const iv = raw.subarray(0, IV_LENGTH);
@@ -51,5 +54,5 @@ export function decrypt(ciphertext: string): string {
let decrypted = decipher.update(encrypted);
const final = decipher.final();
return Buffer.concat([decrypted, final]).toString('utf8');
return Buffer.concat([decrypted, final]).toString("utf8");
}

View File

@@ -1,23 +1,34 @@
import { PaginationQuery, PaginationMeta } from '../types';
import { PaginationQuery, PaginationMeta } from "../types";
export function parsePagination(query: Record<string, unknown>): {
page: number;
limit: number;
skip: number;
sort: string;
order: 'asc' | 'desc';
order: "asc" | "desc";
search: string;
} {
const page = Math.max(1, parseInt(String(query.page || '1'), 10) || 1);
const limit = Math.min(100, Math.max(1, parseInt(String(query.limit || query.per_page || '25'), 10) || 25));
const sort = String(query.sort || 'id');
const order = String(query.order || '').toLowerCase() === 'asc' ? 'asc' : 'desc';
const search = String(query.search || '');
const page = Math.max(1, parseInt(String(query.page || "1"), 10) || 1);
const limit = Math.min(
100,
Math.max(
1,
parseInt(String(query.limit || query.per_page || "25"), 10) || 25,
),
);
const sort = String(query.sort || "id");
const order =
String(query.order || "").toLowerCase() === "asc" ? "asc" : "desc";
const search = String(query.search || "");
return { page, limit, skip: (page - 1) * limit, sort, order, search };
}
export function buildPaginationMeta(total: number, page: number, limit: number): PaginationMeta {
export function buildPaginationMeta(
total: number,
page: number,
limit: number,
): PaginationMeta {
return {
page,
limit,

View File

@@ -1,8 +1,16 @@
import { FastifyReply } from 'fastify';
import { ApiResponse, PaginationMeta } from '../types';
import { FastifyReply } from "fastify";
import { ApiResponse, PaginationMeta } from "../types";
export function success<T>(reply: FastifyReply, data: T, statusCode = 200, message?: string): void {
const response: ApiResponse<T> & { message?: string } = { success: true, data };
export function success<T>(
reply: FastifyReply,
data: T,
statusCode = 200,
message?: string,
): void {
const response: ApiResponse<T> & { message?: string } = {
success: true,
data,
};
if (message) response.message = message;
reply.status(statusCode).send(response);
}
@@ -12,18 +20,26 @@ export function paginated<T>(
data: T,
pagination: PaginationMeta,
): void {
reply.status(200).send({ success: true, data, pagination } satisfies ApiResponse<T>);
reply
.status(200)
.send({ success: true, data, pagination } satisfies ApiResponse<T>);
}
export function error(reply: FastifyReply, message: string, statusCode = 400): void {
reply.status(statusCode).send({ success: false, error: message } satisfies ApiResponse);
export function error(
reply: FastifyReply,
message: string,
statusCode = 400,
): void {
reply
.status(statusCode)
.send({ success: false, error: message } satisfies ApiResponse);
}
/** Parse and validate a numeric ID from route params. Returns NaN-safe number or sends 400 error. */
export function parseId(raw: string, reply: FastifyReply): number | null {
const id = parseInt(raw, 10);
if (isNaN(id) || id <= 0) {
error(reply, 'Neplatné ID', 400);
error(reply, "Neplatné ID", 400);
return null;
}
return id;

View File

@@ -1,5 +1,5 @@
import * as OTPAuthLib from 'otpauth';
import { decrypt } from './encryption';
import * as OTPAuthLib from "otpauth";
import { decrypt } from "./encryption";
export const OTPAuth = {
verify(encryptedSecret: string, code: string): boolean {
@@ -7,7 +7,7 @@ export const OTPAuth = {
const secret = decrypt(encryptedSecret);
const totp = new OTPAuthLib.TOTP({
secret: OTPAuthLib.Secret.fromBase32(secret),
algorithm: 'SHA1',
algorithm: "SHA1",
digits: 6,
period: 30,
});