Fixes every CRITICAL/HIGH finding from the 2026-06-09 full-codebase audit
(REVIEW_FINDINGS.md); each fix went through independent spec + code-quality
review. Plan and per-task log: docs/superpowers/plans/2026-06-10-audit-high-fix-pass.md
- attendance: schemas accept the combined local datetimes the forms/service
use (new dateTimeString helpers in schemas/common.ts), breaks persist on
create, AttendanceCreate submit rebuilt — every submit 400'd since 519edce
- 2fa: backup codes wired to /totp/backup-verify (+ remember-me parity),
enrollment QR generated locally via qrcode (CSP-blocked external service
also leaked the secret), dashboard shows per-user enrollment, not policy
- invoices/orders: per-line VAT survives re-saves (numberOr 0-respecting
coercion in formatters.ts), billing_text persists on update, issued-order
status transitions update UI gates
- trips: real pagination on all 3 pages, GET /trips/stats server aggregate
(shared buildTripsWhere + legacy distance coalesce), vehicle_id applies on
PUT with both-vehicle odometer recompute, print rebuilt (sync window.open,
escaped template, server totals)
- orders api: attachment_data PDF blob excluded from all non-binary reads
- warehouse: unit field is a Select over UnitEnum, receipt attachments
downloadable via new authenticated GET route
- downloads: shared RFC 5987 contentDisposition helper — Czech filenames no
longer 500 (warehouse, received-invoices, orders endpoints)
- misc: block-env hook actually blocks (exit 2 + stderr), project create
works with empty dates, NaN filter guards on trips endpoints
- deps: remove unused concurrently (clears both critical advisories), pin
@hono/node-server >=1.19.13 via overrides (clears the 3 moderates without
the Prisma 6 downgrade), drop deprecated @types stubs
Gates: tsc -b clean - vitest 30 files / 342 tests (31 new) - eslint 0 errors
- build OK
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
99 lines
2.7 KiB
TypeScript
99 lines
2.7 KiB
TypeScript
import { queryOptions } from "@tanstack/react-query";
|
|
import { jsonQuery } from "../apiAdapter";
|
|
|
|
export interface CompanySettingsCustomField {
|
|
name: string;
|
|
value: string;
|
|
showLabel: boolean;
|
|
}
|
|
|
|
export interface CompanySettingsData {
|
|
// Company info
|
|
company_name?: string;
|
|
street?: string;
|
|
city?: string;
|
|
postal_code?: string;
|
|
country?: string;
|
|
company_id?: string;
|
|
vat_id?: string;
|
|
ico?: string;
|
|
dic?: string;
|
|
has_logo?: boolean;
|
|
has_logo_dark?: boolean;
|
|
custom_fields?: CompanySettingsCustomField[];
|
|
supplier_field_order?: string[];
|
|
|
|
// System settings
|
|
require_2fa?: boolean;
|
|
default_currency?: string;
|
|
default_vat_rate?: number;
|
|
available_currencies?: string[];
|
|
available_vat_rates?: number[];
|
|
break_threshold_hours?: number;
|
|
break_duration_short?: number;
|
|
break_duration_long?: number;
|
|
clock_rounding_minutes?: number;
|
|
invoice_alert_email?: string;
|
|
leave_notify_email?: string;
|
|
smtp_from?: string;
|
|
smtp_from_name?: string;
|
|
max_login_attempts?: number;
|
|
lockout_minutes?: number;
|
|
max_requests_per_minute?: number;
|
|
|
|
// Numbering
|
|
quotation_prefix?: string;
|
|
order_type_code?: string;
|
|
invoice_type_code?: string;
|
|
offer_number_pattern?: string;
|
|
order_number_pattern?: string;
|
|
invoice_number_pattern?: string;
|
|
issued_order_number_pattern?: string;
|
|
issued_order_type_code?: string;
|
|
project_number_pattern?: string;
|
|
project_type_code?: string;
|
|
warehouse_receipt_prefix?: string;
|
|
warehouse_receipt_number_pattern?: string;
|
|
warehouse_issue_prefix?: string;
|
|
warehouse_issue_number_pattern?: string;
|
|
warehouse_inventory_prefix?: string;
|
|
warehouse_inventory_number_pattern?: string;
|
|
|
|
// Misc
|
|
app_version?: string;
|
|
}
|
|
|
|
export const companySettingsOptions = () =>
|
|
queryOptions({
|
|
queryKey: ["company-settings"],
|
|
queryFn: () =>
|
|
jsonQuery<CompanySettingsData>("/api/admin/company-settings"),
|
|
staleTime: 5 * 60_000,
|
|
});
|
|
|
|
export const systemInfoOptions = () =>
|
|
queryOptions({
|
|
queryKey: ["settings", "system-info"],
|
|
queryFn: () =>
|
|
jsonQuery<Record<string, unknown>>(
|
|
"/api/admin/company-settings/system-info",
|
|
),
|
|
});
|
|
|
|
export const require2FAOptions = () =>
|
|
queryOptions({
|
|
queryKey: ["settings", "2fa"],
|
|
queryFn: () =>
|
|
jsonQuery<{ require_2fa: boolean }>("/api/admin/totp/required"),
|
|
});
|
|
|
|
// Per-user 2FA enrollment status (users.totp_enabled) — NOT the company-wide
|
|
// require_2fa policy above. Mutations that enable/disable 2FA must invalidate
|
|
// the broad ["totp"] domain key.
|
|
export const totpStatusOptions = () =>
|
|
queryOptions({
|
|
queryKey: ["totp", "status"],
|
|
queryFn: () =>
|
|
jsonQuery<{ totp_enabled: boolean }>("/api/admin/totp/status"),
|
|
});
|