From 654aa775db72e4cbc9aa8b2ec25c1daf8ba05d68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joe=20K=C3=BCng?= Date: Tue, 3 Mar 2026 10:11:12 +0100 Subject: [PATCH 1/2] fix(back-end): implementation of security better --- .../com/printcalculator/BackendApplication.java | 4 ++-- .../printcalculator/config/SecurityConfig.java | 2 ++ .../controller/QuoteSessionController.java | 2 +- .../service/QuoteCalculator.java | 17 +++++++++++++++++ .../src/main/resources/application.properties | 4 ++++ frontend/src/assets/i18n/de.json | 2 ++ frontend/src/assets/i18n/en.json | 2 ++ frontend/src/assets/i18n/fr.json | 2 ++ 8 files changed, 32 insertions(+), 3 deletions(-) diff --git a/backend/src/main/java/com/printcalculator/BackendApplication.java b/backend/src/main/java/com/printcalculator/BackendApplication.java index c528427..5203f11 100644 --- a/backend/src/main/java/com/printcalculator/BackendApplication.java +++ b/backend/src/main/java/com/printcalculator/BackendApplication.java @@ -2,12 +2,12 @@ package com.printcalculator; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.EnableAsync; -import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.transaction.annotation.EnableTransactionManagement; -@SpringBootApplication +@SpringBootApplication(exclude = {UserDetailsServiceAutoConfiguration.class}) @EnableTransactionManagement @EnableScheduling @EnableAsync diff --git a/backend/src/main/java/com/printcalculator/config/SecurityConfig.java b/backend/src/main/java/com/printcalculator/config/SecurityConfig.java index 5f0c81c..e7e6670 100644 --- a/backend/src/main/java/com/printcalculator/config/SecurityConfig.java +++ b/backend/src/main/java/com/printcalculator/config/SecurityConfig.java @@ -29,6 +29,8 @@ public class SecurityConfig { .logout(AbstractHttpConfigurer::disable) .authorizeHttpRequests(auth -> auth .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() + .requestMatchers("/actuator/health", "/actuator/health/**").permitAll() + .requestMatchers("/actuator/**").denyAll() .requestMatchers("/api/admin/auth/login").permitAll() .requestMatchers("/api/admin/**").authenticated() .anyRequest().permitAll() diff --git a/backend/src/main/java/com/printcalculator/controller/QuoteSessionController.java b/backend/src/main/java/com/printcalculator/controller/QuoteSessionController.java index 3119cb1..50a6329 100644 --- a/backend/src/main/java/com/printcalculator/controller/QuoteSessionController.java +++ b/backend/src/main/java/com/printcalculator/controller/QuoteSessionController.java @@ -97,7 +97,7 @@ public class QuoteSessionController { session.setExpiresAt(OffsetDateTime.now().plusDays(30)); var policy = pricingRepo.findFirstByIsActiveTrueOrderByValidFromDesc(); - session.setSetupCostChf(policy != null ? policy.getFixedJobFeeChf() : BigDecimal.ZERO); + session.setSetupCostChf(quoteCalculator.calculateSessionSetupFee(policy)); session = sessionRepo.save(session); return ResponseEntity.ok(session); diff --git a/backend/src/main/java/com/printcalculator/service/QuoteCalculator.java b/backend/src/main/java/com/printcalculator/service/QuoteCalculator.java index 04fb5ea..4dc4f82 100644 --- a/backend/src/main/java/com/printcalculator/service/QuoteCalculator.java +++ b/backend/src/main/java/com/printcalculator/service/QuoteCalculator.java @@ -21,6 +21,8 @@ import java.util.List; @Service public class QuoteCalculator { + private static final BigDecimal SETUP_FEE_DOUBLE_THRESHOLD_CHF = BigDecimal.TEN; + private static final BigDecimal SETUP_FEE_MULTIPLIER_BELOW_THRESHOLD = BigDecimal.valueOf(2); private final PricingPolicyRepository pricingRepo; private final PricingPolicyMachineHourTierRepository tierRepo; @@ -111,6 +113,21 @@ public class QuoteCalculator { return rawCost.multiply(markupFactor).setScale(2, RoundingMode.HALF_UP); } + public BigDecimal calculateSessionSetupFee(PricingPolicy policy) { + if (policy == null || policy.getFixedJobFeeChf() == null) { + return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); + } + + BigDecimal baseSetupFee = policy.getFixedJobFeeChf(); + if (baseSetupFee.compareTo(SETUP_FEE_DOUBLE_THRESHOLD_CHF) < 0) { + return baseSetupFee + .multiply(SETUP_FEE_MULTIPLIER_BELOW_THRESHOLD) + .setScale(2, RoundingMode.HALF_UP); + } + + return baseSetupFee.setScale(2, RoundingMode.HALF_UP); + } + private BigDecimal calculateMachineCost(PricingPolicy policy, BigDecimal hours) { List tiers = tierRepo.findAllByPricingPolicyOrderByTierStartHoursAsc(policy); if (tiers.isEmpty()) { diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index d636a7e..a24a15c 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -7,6 +7,7 @@ spring.datasource.username=${DB_USERNAME:printcalc} spring.datasource.password=${DB_PASSWORD:printcalc_secret} spring.jpa.hibernate.ddl-auto=update spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect +spring.jpa.open-in-view=false # Slicer Configuration @@ -47,3 +48,6 @@ admin.password=${ADMIN_PASSWORD} admin.session.secret=${ADMIN_SESSION_SECRET} admin.session.ttl-minutes=${ADMIN_SESSION_TTL_MINUTES:480} admin.auth.trust-proxy-headers=${ADMIN_AUTH_TRUST_PROXY_HEADERS:false} + +# Expose only liveness endpoint by default. +management.endpoints.web.exposure.include=health diff --git a/frontend/src/assets/i18n/de.json b/frontend/src/assets/i18n/de.json index 8fd59c1..b6b0b00 100644 --- a/frontend/src/assets/i18n/de.json +++ b/frontend/src/assets/i18n/de.json @@ -133,6 +133,8 @@ "TITLE": "Über uns", "EYEBROW": "3D-Druck-Labor", "SUBTITLE": "Wir sind zwei Studenten mit viel Motivation und Lernbereitschaft.", + "HOW_TEXT": "3D Fab ist entstanden, um das Potenzial des 3D-Drucks in alltagstaugliche Lösungen zu verwandeln. Wir haben mit technischer Neugier begonnen und sind zur Herstellung von Ersatzteilen, Produkten und maßgeschneiderten Prototypen gekommen. Um von einer Idee zu einem konkreten Projekt zu gelangen, haben wir unseren automatischen Rechner eingeführt: klare Angebote in einem Klick, damit Sie einen professionellen Service ohne Preisüberraschungen erhalten.", + "PASSIONS_TITLE": "Unsere Interessen", "PASSION_BIKE_TRIAL": "Bike Trial", "PASSION_MOUNTAIN": "Berge", "PASSION_SKI": "Ski", diff --git a/frontend/src/assets/i18n/en.json b/frontend/src/assets/i18n/en.json index 2430c61..a61b7d3 100644 --- a/frontend/src/assets/i18n/en.json +++ b/frontend/src/assets/i18n/en.json @@ -133,6 +133,8 @@ "TITLE": "About Us", "EYEBROW": "3D Printing Lab", "SUBTITLE": "We are two students with a strong desire to build and learn.", + "HOW_TEXT": "3D Fab was created to turn the potential of 3D printing into everyday solutions. We started from technical curiosity and grew into producing spare parts, products, and custom prototypes. To move from an idea to a concrete project, we launched our automatic calculator: clear quotes in one click, so you get a professional service with no price surprises.", + "PASSIONS_TITLE": "Our interests", "PASSION_BIKE_TRIAL": "Bike trial", "PASSION_MOUNTAIN": "Mountain", "PASSION_SKI": "Ski", diff --git a/frontend/src/assets/i18n/fr.json b/frontend/src/assets/i18n/fr.json index eb945dd..e9f3ad4 100644 --- a/frontend/src/assets/i18n/fr.json +++ b/frontend/src/assets/i18n/fr.json @@ -190,6 +190,8 @@ "TITLE": "Qui sommes-nous", "EYEBROW": "Atelier d'impression 3D", "SUBTITLE": "Nous sommes deux étudiants avec beaucoup d'envie de faire et d'apprendre.", + "HOW_TEXT": "3D Fab est né pour transformer le potentiel de l'impression 3D en solutions du quotidien. Nous sommes partis de la curiosité technique et nous sommes arrivés à la production de pièces de rechange, de produits et de prototypes sur mesure. Pour passer d'une idée à un projet concret, nous avons lancé notre calculateur automatique : des devis clairs en un clic pour vous garantir un service professionnel, sans surprises sur le prix.", + "PASSIONS_TITLE": "Nos centres d'intérêt", "PASSION_BIKE_TRIAL": "Bike trial", "PASSION_MOUNTAIN": "Montagne", "PASSION_SKI": "Ski", -- 2.49.1 From 5c27d4d16b0501421cbeae9098fae5d100f7ac77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joe=20K=C3=BCng?= Date: Tue, 3 Mar 2026 10:24:21 +0100 Subject: [PATCH 2/2] feat(back-end): pla tought --- .../controller/QuoteSessionController.java | 16 +++++++-- .../printcalculator/dto/PrintSettingsDto.java | 2 +- .../service/OrcaProfileResolver.java | 1 + .../service/ProfileManager.java | 1 + .../service/QuoteCalculator.java | 1 + db.sql | 35 ++++++++++++++++++- .../pages/admin-filament-stock.component.html | 2 +- 7 files changed, 52 insertions(+), 6 deletions(-) diff --git a/backend/src/main/java/com/printcalculator/controller/QuoteSessionController.java b/backend/src/main/java/com/printcalculator/controller/QuoteSessionController.java index 50a6329..58ccb50 100644 --- a/backend/src/main/java/com/printcalculator/controller/QuoteSessionController.java +++ b/backend/src/main/java/com/printcalculator/controller/QuoteSessionController.java @@ -296,9 +296,7 @@ public class QuoteSessionController { return variant; } - String requestedMaterialCode = settings.getMaterial() != null - ? settings.getMaterial().trim().toUpperCase() - : "PLA"; + String requestedMaterialCode = normalizeRequestedMaterialCode(settings.getMaterial()); FilamentMaterialType materialType = materialRepo.findByMaterialCode(requestedMaterialCode) .orElseGet(() -> materialRepo.findByMaterialCode("PLA") @@ -316,6 +314,18 @@ public class QuoteSessionController { .orElseThrow(() -> new RuntimeException("No active variant for material: " + requestedMaterialCode)); } + private String normalizeRequestedMaterialCode(String value) { + if (value == null || value.isBlank()) { + return "PLA"; + } + + return value.trim() + .toUpperCase(Locale.ROOT) + .replace('_', ' ') + .replace('-', ' ') + .replaceAll("\\s+", " "); + } + // 3. Update Line Item @PatchMapping("/line-items/{lineItemId}") @Transactional diff --git a/backend/src/main/java/com/printcalculator/dto/PrintSettingsDto.java b/backend/src/main/java/com/printcalculator/dto/PrintSettingsDto.java index 5c56ced..e01cfc6 100644 --- a/backend/src/main/java/com/printcalculator/dto/PrintSettingsDto.java +++ b/backend/src/main/java/com/printcalculator/dto/PrintSettingsDto.java @@ -8,7 +8,7 @@ public class PrintSettingsDto { private String complexityMode; // Common - private String material; // e.g. "PLA", "PETG" + private String material; // e.g. "PLA", "PLA TOUGH", "PETG" private String color; // e.g. "White", "#FFFFFF" private Long filamentVariantId; private Long printerMachineId; diff --git a/backend/src/main/java/com/printcalculator/service/OrcaProfileResolver.java b/backend/src/main/java/com/printcalculator/service/OrcaProfileResolver.java index 26f0d88..b1f35dd 100644 --- a/backend/src/main/java/com/printcalculator/service/OrcaProfileResolver.java +++ b/backend/src/main/java/com/printcalculator/service/OrcaProfileResolver.java @@ -116,6 +116,7 @@ public class OrcaProfileResolver { : "PLA"; return switch (materialCode) { + case "PLA TOUGH" -> "Bambu PLA Tough @BBL A1"; case "PETG" -> "Generic PETG"; case "TPU" -> "Generic TPU"; case "PC" -> "Generic PC"; diff --git a/backend/src/main/java/com/printcalculator/service/ProfileManager.java b/backend/src/main/java/com/printcalculator/service/ProfileManager.java index 3737d43..67eee52 100644 --- a/backend/src/main/java/com/printcalculator/service/ProfileManager.java +++ b/backend/src/main/java/com/printcalculator/service/ProfileManager.java @@ -46,6 +46,7 @@ public class ProfileManager { // Material Aliases profileAliases.put("pla_basic", "Bambu PLA Basic @BBL A1"); + profileAliases.put("pla_tough", "Bambu PLA Tough @BBL A1"); profileAliases.put("petg_basic", "Bambu PETG Basic @BBL A1"); profileAliases.put("tpu_95a", "Bambu TPU 95A @BBL A1"); diff --git a/backend/src/main/java/com/printcalculator/service/QuoteCalculator.java b/backend/src/main/java/com/printcalculator/service/QuoteCalculator.java index 4dc4f82..5b8dbdf 100644 --- a/backend/src/main/java/com/printcalculator/service/QuoteCalculator.java +++ b/backend/src/main/java/com/printcalculator/service/QuoteCalculator.java @@ -175,6 +175,7 @@ public class QuoteCalculator { private String detectMaterialCode(String profileName) { String lower = profileName.toLowerCase(); + if (lower.contains("pla tough") || lower.contains("pla_tough")) return "PLA TOUGH"; if (lower.contains("petg")) return "PETG"; if (lower.contains("tpu")) return "TPU"; if (lower.contains("abs")) return "ABS"; diff --git a/db.sql b/db.sql index ee9422d..8b1fe79 100644 --- a/db.sql +++ b/db.sql @@ -285,6 +285,7 @@ insert into filament_material_type (material_code, is_technical, technical_type_label) values ('PLA', false, false, null), + ('PLA TOUGH', false, false, null), ('PETG', false, false, null), ('TPU', true, false, null), ('PC', false, true, 'engineering'), @@ -355,6 +356,37 @@ on conflict (filament_material_type_id, variant_display_name) do update is_active = excluded.is_active; -- Varianti base per materiali principali del calcolatore +with mat as (select filament_material_type_id + from filament_material_type + where material_code = 'PLA TOUGH') +insert +into filament_variant (filament_material_type_id, variant_display_name, color_name, color_hex, finish_type, brand, + is_matte, is_special, cost_chf_per_kg, stock_spools, spool_net_kg, is_active) +select mat.filament_material_type_id, + 'PLA Tough Nero', + 'Nero', + '#1A1A1A', + 'GLOSSY', + 'Bambu', + false, + false, + 18.00, + 1.000, + 1.000, + true +from mat +on conflict (filament_material_type_id, variant_display_name) do update + set color_name = excluded.color_name, + color_hex = excluded.color_hex, + finish_type = excluded.finish_type, + brand = excluded.brand, + is_matte = excluded.is_matte, + is_special = excluded.is_special, + cost_chf_per_kg = excluded.cost_chf_per_kg, + stock_spools = excluded.stock_spools, + spool_net_kg = excluded.spool_net_kg, + is_active = excluded.is_active; + with mat as (select filament_material_type_id from filament_material_type where material_code = 'PETG') @@ -491,13 +523,14 @@ with p as (select printer_machine_profile_id and pmp.nozzle_diameter_mm = 0.40::numeric), m as (select filament_material_type_id, material_code from filament_material_type - where material_code in ('PLA', 'PETG', 'TPU', 'PC')) + where material_code in ('PLA', 'PLA TOUGH', 'PETG', 'TPU', 'PC')) insert into material_orca_profile_map (printer_machine_profile_id, filament_material_type_id, orca_filament_profile_name, is_active) select p.printer_machine_profile_id, m.filament_material_type_id, case m.material_code when 'PLA' then 'Bambu PLA Basic @BBL A1' + when 'PLA TOUGH' then 'Bambu PLA Tough @BBL A1' when 'PETG' then 'Bambu PETG Basic @BBL A1' when 'TPU' then 'Bambu TPU 95A @BBL A1' when 'PC' then 'Generic PC @BBL A1' diff --git a/frontend/src/app/features/admin/pages/admin-filament-stock.component.html b/frontend/src/app/features/admin/pages/admin-filament-stock.component.html index 051e71d..d4dfd8d 100644 --- a/frontend/src/app/features/admin/pages/admin-filament-stock.component.html +++ b/frontend/src/app/features/admin/pages/admin-filament-stock.component.html @@ -316,7 +316,7 @@

Sei sicuro?

-

Vuoi eliminare la variante {{ variantToDelete?.variantDisplayName }}?

+

Vuoi eliminare la variante {{ variantToDelete.variantDisplayName }}?

L'operazione non è reversibile.

-- 2.49.1