Subversion Repositories SmartDukaan

Rev

Rev 37823 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
21561 ashik.ali 1
package com.spice.profitmandi.web.config;
21555 kshitij.so 2
 
32569 amit.gupta 3
import com.fasterxml.jackson.core.JsonParser;
37161 ranu 4
import com.fasterxml.jackson.databind.*;
37072 amit 5
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
32569 amit.gupta 6
import com.fasterxml.jackson.databind.deser.std.StringDeserializer;
7
import com.fasterxml.jackson.databind.module.SimpleModule;
31282 amit.gupta 8
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
9
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
33618 tejus.loha 10
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
31282 amit.gupta 11
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
33618 tejus.loha 12
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
31282 amit.gupta 13
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
14
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
29993 amit.gupta 15
import com.google.gson.Gson;
16
import com.google.gson.GsonBuilder;
37513 ranu 17
import com.spice.profitmandi.common.services.AuthenticatedIdentityMailSender;
37630 amit 18
import com.spice.profitmandi.common.services.MailRecipientFilter;
19
import com.spice.profitmandi.common.services.RecipientFilteringMailSender;
29993 amit.gupta 20
import com.spice.profitmandi.common.util.Utils;
21
import com.spice.profitmandi.dao.convertor.LocalDateTimeJsonConverter;
23869 amit.gupta 22
import org.apache.logging.log4j.LogManager;
23568 govind 23
import org.apache.logging.log4j.Logger;
28339 tejbeer 24
import org.apache.velocity.app.VelocityEngine;
25
import org.apache.velocity.exception.VelocityException;
21555 kshitij.so 26
import org.springframework.context.annotation.Bean;
27
import org.springframework.context.annotation.ComponentScan;
37630 amit 28
import org.springframework.context.annotation.Lazy;
21555 kshitij.so 29
import org.springframework.context.annotation.Configuration;
37476 amit 30
import org.springframework.context.annotation.Primary;
21555 kshitij.so 31
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
32
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
33
import org.springframework.core.io.ClassPathResource;
34
import org.springframework.core.io.Resource;
23985 tejbeer 35
import org.springframework.mail.javamail.JavaMailSender;
36
import org.springframework.mail.javamail.JavaMailSenderImpl;
28339 tejbeer 37
import org.springframework.ui.velocity.VelocityEngineFactoryBean;
21555 kshitij.so 38
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
21625 kshitij.so 39
import org.springframework.web.servlet.ViewResolver;
24507 amit.gupta 40
import org.springframework.web.servlet.view.BeanNameViewResolver;
21625 kshitij.so 41
import org.springframework.web.servlet.view.velocity.VelocityConfigurer;
31513 amit.gupta 42
import org.springframework.web.servlet.view.velocity.VelocityLayoutViewResolver;
21555 kshitij.so 43
 
29993 amit.gupta 44
import java.io.IOException;
45
import java.math.RoundingMode;
46
import java.text.DecimalFormat;
47
import java.text.NumberFormat;
33618 tejus.loha 48
import java.time.LocalDate;
29993 amit.gupta 49
import java.time.LocalDateTime;
50
import java.time.format.DateTimeFormatter;
31282 amit.gupta 51
import java.time.format.DateTimeFormatterBuilder;
29993 amit.gupta 52
import java.util.HashMap;
53
import java.util.Locale;
54
import java.util.Map;
55
import java.util.Properties;
25300 tejbeer 56
 
21625 kshitij.so 57
@SuppressWarnings("deprecation")
21555 kshitij.so 58
@Configuration
59
@ComponentScan("com.spice.profitmandi.*")
31511 amit.gupta 60
//@Import(RepositoryRestMvcConfiguration.class)
21555 kshitij.so 61
public class AppConfig {
62
 
32054 tejbeer 63
    private static final String VELOCITY_PATH_PREFIX = "/WEB-INF/views/ftl/";
64
    private static final String VELOCITY_PATH_SUFFIX = ".vm";
32773 raveendra. 65
    private static final String MESSAGE_PATH_SOURCE_NAME = "classpath:messages";
32054 tejbeer 66
    private static final Logger LOGGER = LogManager.getLogger(AppConfig.class);
67
    private static Resource resource;
35832 amit 68
    private static Resource sharedResource;
24639 tejbeer 69
 
32054 tejbeer 70
    public static Resource getResource() {
71
        return resource;
72
    }
21555 kshitij.so 73
 
35832 amit 74
    public static Resource getSharedResource() {
75
        return sharedResource;
76
    }
77
 
32054 tejbeer 78
    public void setResource(Resource resource) {
79
        AppConfig.resource = resource;
80
    }
21625 kshitij.so 81
 
32054 tejbeer 82
    @Bean(value = "velocityAttributesMap")
83
    public Map<String, Object> velocityAttributesMap() {
84
        Map<String, Object> attributesMap = new HashMap<>();
85
        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
86
        DateTimeFormatter dateMonthFormatter = DateTimeFormatter.ofPattern("dd''MMM");
87
        DateTimeFormatter datehiphenFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
88
        DateTimeFormatter dateMonthYearFormatter = DateTimeFormatter.ofPattern("MM-yyyy");
89
        DateTimeFormatter dateYearMonthFormatter = DateTimeFormatter.ofPattern("MMM''uu");
31218 tejbeer 90
 
32054 tejbeer 91
        DateTimeFormatter dateMonthChYear = DateTimeFormatter.ofPattern("d''MMM''uu");
92
        DecimalFormat decimalFormatter = new DecimalFormat("0.#");
30859 tejbeer 93
 
32054 tejbeer 94
        NumberFormat numberformat = NumberFormat.getNumberInstance(new Locale("en", "IN"));
30859 tejbeer 95
 
32054 tejbeer 96
        numberformat.setRoundingMode(RoundingMode.HALF_DOWN);
97
        numberformat.setMaximumFractionDigits(2);
98
        numberformat.setMinimumFractionDigits(0);
37774 amit 99
 
100
        NumberFormat wholeNumberFormat = NumberFormat.getNumberInstance(new Locale("en", "IN"));
101
        wholeNumberFormat.setRoundingMode(RoundingMode.HALF_UP);
102
        wholeNumberFormat.setMaximumFractionDigits(0);
32054 tejbeer 103
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
104
        attributesMap.put("dateFormatter", dateFormatter);
105
        attributesMap.put("nf", numberformat);
37774 amit 106
        attributesMap.put("nf0", wholeNumberFormat);
32054 tejbeer 107
        attributesMap.put("dateMonthFormatter", dateMonthFormatter);
108
        attributesMap.put("datehiphenFormatter", datehiphenFormatter);
109
        attributesMap.put("dateYearMonthFormatter", dateYearMonthFormatter);
31218 tejbeer 110
 
32054 tejbeer 111
        attributesMap.put("dateMonthYearFormatter", dateMonthYearFormatter);
112
        attributesMap.put("dateMonthChYear", dateMonthChYear);
113
        attributesMap.put("decimalFormatter", decimalFormatter);
114
        attributesMap.put("noData", "<tr><td colspan=\"20\" style=\"text-align:center;\">No results found for the given criteria</td></tr>");
115
        attributesMap.put("dateTimeFormatter", dateTimeFormatter);
37846 amit 116
        attributesMap.put("version", "442");
37523 ranu 117
        attributesMap.put("cssVersion", "53");
32054 tejbeer 118
        attributesMap.put("isDev", getActiveProfile().equals("dev"));
119
        attributesMap.put("vmUtils", new Utils());
32200 amit.gupta 120
        //attributesMap.put("esc", new EscapeTool());
32054 tejbeer 121
        attributesMap.put("ru", RoundingMode.HALF_UP);
122
        return attributesMap;
123
    }
30470 amit.gupta 124
 
32054 tejbeer 125
    @Bean
126
    public ViewResolver viewResolver() {
127
        VelocityLayoutViewResolver bean = new VelocityLayoutViewResolver();
128
        bean.setPrefix("");
129
        bean.setSuffix(VELOCITY_PATH_SUFFIX);
35587 ranu 130
        bean.setContentType("text/html;charset=UTF-8");
32054 tejbeer 131
        bean.setRequestContextAttribute("rc");
32773 raveendra. 132
        bean.setExposeSpringMacroHelpers(true);
32054 tejbeer 133
        bean.setAttributesMap(this.velocityAttributesMap());
134
        return bean;
135
    }
21625 kshitij.so 136
 
32054 tejbeer 137
    @Bean
138
    public ViewResolver beanNameViewResolver() {
139
        BeanNameViewResolver resolver = new BeanNameViewResolver();
140
        return resolver;
141
    }
24507 amit.gupta 142
 
32054 tejbeer 143
    @Bean
144
    public VelocityConfigurer velocityConfig() {
145
        VelocityConfigurer velocityConfigurer = new VelocityConfigurer();
146
        Properties velocityProperties = new Properties();
35294 ranu 147
       /* velocityProperties.put("velocimacro.library", "macros.vm");
35289 amit 148
        velocityProperties.put("velocimacro.permissions.allow.inline", "true");
35294 ranu 149
        velocityProperties.put("velocimacro.permissions.allow.inline.to.replace.global", "true");*/
32054 tejbeer 150
        velocityProperties.put("directive.set.null.allowed", "true");
36686 ranu 151
        // Read .vm files as UTF-8 and emit UTF-8 — otherwise non-ASCII chars
152
        // (emoji, accents) in templates render as mojibake (e.g. 📷 → ð).
153
        velocityProperties.put("input.encoding", "UTF-8");
154
        velocityProperties.put("output.encoding", "UTF-8");
32054 tejbeer 155
        velocityConfigurer.setVelocityProperties(velocityProperties);
156
        velocityConfigurer.setResourceLoaderPath(VELOCITY_PATH_PREFIX);
157
        return velocityConfigurer;
158
    }
21555 kshitij.so 159
 
32788 amit.gupta 160
    @Bean(name = "messageSource")
161
    public ReloadableResourceBundleMessageSource getReloadableResourceBundleMessageSource() {
32054 tejbeer 162
        LOGGER.debug("creating messageSource bean with message path source name : " + MESSAGE_PATH_SOURCE_NAME);
163
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
164
        messageSource.setBasename(MESSAGE_PATH_SOURCE_NAME);
32773 raveendra. 165
        messageSource.setDefaultEncoding("UTF-8");
166
 
32054 tejbeer 167
        return messageSource;
168
    }
21555 kshitij.so 169
 
32054 tejbeer 170
    @Bean(name = "multipartResolver")
171
    public CommonsMultipartResolver getCommonsMultipartResolver() {
172
        LOGGER.info("creating common multipart resolver bean");
173
        return new CommonsMultipartResolver();
174
    }
21555 kshitij.so 175
 
32054 tejbeer 176
    @Bean
177
    public PropertySourcesPlaceholderConfigurer propertyConfigurer1() {
178
        String activeProfile = getActiveProfile();
21555 kshitij.so 179
 
32054 tejbeer 180
        PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
21625 kshitij.so 181
 
35381 amit 182
        Resource appResource;
183
        Resource sharedResource;
184
 
32054 tejbeer 185
        if ("prod".equals(activeProfile)) {
35381 amit 186
            appResource = new ClassPathResource("/META-INF/prod.properties");
187
            sharedResource = new ClassPathResource("/shared-prod.properties");
32054 tejbeer 188
        } else if ("staging".equals(activeProfile)) {
35381 amit 189
            appResource = new ClassPathResource("/META-INF/staging.properties");
190
            sharedResource = new ClassPathResource("/shared-staging.properties");
32054 tejbeer 191
        } else {
35381 amit 192
            appResource = new ClassPathResource("/META-INF/dev.properties");
193
            sharedResource = new ClassPathResource("/shared-dev.properties");
32054 tejbeer 194
        }
21555 kshitij.so 195
 
35381 amit 196
        resource = appResource;
35832 amit 197
        AppConfig.sharedResource = sharedResource;
21555 kshitij.so 198
 
35381 amit 199
        // Load shared properties from dao first, then app-specific (app-specific can override)
200
        propertySourcesPlaceholderConfigurer.setLocations(sharedResource, appResource);
201
        propertySourcesPlaceholderConfigurer.setIgnoreResourceNotFound(true);
202
 
32054 tejbeer 203
        return propertySourcesPlaceholderConfigurer;
204
    }
21625 kshitij.so 205
 
32054 tejbeer 206
    private String getActiveProfile() {
207
        Properties properties = new Properties();
208
        try {
209
            properties.load(this.getClass().getClassLoader().getResourceAsStream("META-INF/env.property"));
210
        } catch (IOException e) {
211
            LOGGER.error("Error in reading env property file.Adding default property" + e);
212
            properties.put("profile", "dev");
213
        }
214
        LOGGER.info("Profile is [{}]", properties.get("profile"));
215
        return (String) properties.get("profile");
216
    }
25811 amit.gupta 217
 
37479 amit 218
    /**
219
     * The default sender.
220
     *
221
     * Verified working 2026-08-31: MAIL FROM:<noreply@smartdukaan.com> returns 250
222
     * over IPv4. It does not authenticate -- Workspace authorises it by allowlisted
223
     * source IP -- so it may legitimately send as noreply@.
224
     *
37480 amit 225
     * googleMailSender is the alternative and its credentials are valid -- verified
226
     * 2026-08-31, AUTH OK on both 465 and 587. Use it by qualifier where a message
227
     * must come from the authenticated identity rather than noreply@.
37479 amit 228
     *
37480 amit 229
     * SMTP on this host works over IPv4 only, and that is not specific to the relay:
230
     * over IPv6 the relay answers "550 5.7.1 Invalid credentials for relay" and
231
     * smtp.gmail.com rejects a valid app password with "535 5.7.8 Username and
232
     * Password not accepted". The JVM prefers IPv4, which is the only reason mail
233
     * leaves this box at all. Anything that prefers IPv6 will fail on both paths.
37479 amit 234
     */
235
    @Bean(name = {"gmailRelaySender", "mailSender"})
236
    @Primary
37630 amit 237
    public JavaMailSender getGmailRelaySender(@Lazy MailRecipientFilter mailRecipientFilter) {
238
        RecipientFilteringMailSender mailSender = new RecipientFilteringMailSender();
239
        mailSender.setRecipientFilter(mailRecipientFilter);
36247 amit 240
        mailSender.setHost("smtp-relay.gmail.com");
241
        mailSender.setPort(587);
34667 amit.gupta 242
 
36247 amit 243
        Properties props = mailSender.getJavaMailProperties();
244
        props.put("mail.smtp.starttls.enable", "true");
245
        props.put("mail.smtp.auth", "false");
246
        props.put("mail.smtp.localhost", "smartdukaan.com");
247
        props.put("mail.smtp.ssl.protocols", "TLSv1.2");
248
        props.put("mail.smtp.ssl.trust", "smtp-relay.gmail.com");
249
        props.put("mail.transport.protocol", "smtp");
37365 amit 250
        props.put("mail.smtp.connectiontimeout", "30000");
251
        props.put("mail.smtp.timeout", "120000");
252
        props.put("mail.smtp.writetimeout", "120000");
36247 amit 253
 
254
        return mailSender;
255
    }
256
 
257
 
37476 amit 258
    /**
259
     * The default sender. Authenticates as sdtech@smartdukaan.com and rewrites every
260
     * From to that identity — Workspace binds an authenticated session to one address
261
     * and rejects any other. Also answers to "mailSender" so unqualified
262
     * JavaMailSender injections resolve here.
263
     */
37479 amit 264
    @Bean(name = "googleMailSender")
37630 amit 265
    public JavaMailSender googleMailSender(@Lazy MailRecipientFilter mailRecipientFilter) {
266
        AuthenticatedIdentityMailSender mailSender = new AuthenticatedIdentityMailSender();
267
        mailSender.setRecipientFilter(mailRecipientFilter);
34636 ranu 268
        mailSender.setHost("smtp.gmail.com");
269
        mailSender.setPort(465);
270
        mailSender.setUsername("sdtech@smartdukaan.com");
37369 amit 271
        mailSender.setPassword("wbdwyqrcgosrkwdn"); // App Password
34636 ranu 272
 
273
        Properties props = mailSender.getJavaMailProperties();
274
        props.put("mail.smtp.auth", "true");
275
        props.put("mail.smtp.ssl.enable", "true");
276
        props.put("mail.smtp.ssl.trust", "smtp.gmail.com");
277
        props.put("mail.smtp.socketFactory.port", "465");
278
        props.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
37365 amit 279
        props.put("mail.smtp.connectiontimeout", "30000");
280
        props.put("mail.smtp.timeout", "120000");
281
        props.put("mail.smtp.writetimeout", "120000");
34636 ranu 282
 
283
        return mailSender;
284
    }
285
 
32054 tejbeer 286
    @Bean(name = "gson")
287
    public Gson gson() {
25300 tejbeer 288
 
32054 tejbeer 289
        Gson gson = new GsonBuilder().serializeNulls().registerTypeAdapter(LocalDateTime.class, new LocalDateTimeJsonConverter()).create();
25300 tejbeer 290
 
32054 tejbeer 291
        return gson;
25300 tejbeer 292
 
32054 tejbeer 293
    }
25300 tejbeer 294
 
32054 tejbeer 295
    @Bean(name = "veloctyEngine")
296
    public VelocityEngine velocityEngine() throws VelocityException, IOException {
297
        VelocityEngineFactoryBean factory = new VelocityEngineFactoryBean();
298
        // Properties props = new Properties();
299
        // props.put("resource.loader", "file");
28339 tejbeer 300
 
32054 tejbeer 301
        // props.put("file.resource.loader.description", "Velocity File Resource
302
        // Loader");
303
        // props.put("file.resource.loader.class",
304
        // "org.apache.velocity.runtime.resource.loader.FileResourceLoader");
305
        // props.put("file.resource.loader.cache", true);
306
        // props.put("file.resource.loader.path", ".");
32773 raveendra. 307
        factory.setPreferFileSystemAccess(false);
308
        // factory.setResourceLoaderPath("classpath:/templates/");
28339 tejbeer 309
 
32773 raveendra. 310
 
32054 tejbeer 311
        Properties velocityProperties = new Properties();
312
        velocityProperties.put("resource.loader", "class");
313
        velocityProperties.put("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
314
        velocityProperties.put("file.resource.loader.cache", true);
315
        velocityProperties.put("file.resource.loader.unicode", true);
316
        velocityProperties.put("input.encoding", "UTF-8");
317
        velocityProperties.put("output.encoding", "UTF-8");
318
        velocityProperties.put("overrideLogging", true);
32773 raveendra. 319
        velocityProperties.setProperty("input.encoding", "UTF-8");
320
        velocityProperties.setProperty("output.encoding", "UTF-8");
321
        factory.setVelocityProperties(velocityProperties);
28339 tejbeer 322
 
32054 tejbeer 323
        // velocityProperties.put("file.resource.loader.path", ".");
28339 tejbeer 324
 
32054 tejbeer 325
        factory.setVelocityProperties(velocityProperties);
326
        return factory.createVelocityEngine();
28339 tejbeer 327
 
32054 tejbeer 328
    }
28339 tejbeer 329
 
32054 tejbeer 330
    @Bean
331
    public ObjectMapper objectMapper() {
332
        DateTimeFormatter df = new DateTimeFormatterBuilder().parseCaseInsensitive().append(DateTimeFormatter.ISO_LOCAL_DATE).optionalStart().appendLiteral('T').optionalEnd().appendLiteral(' ').append(DateTimeFormatter.ISO_LOCAL_TIME).toFormatter();
333
        DateTimeFormatter sf = new DateTimeFormatterBuilder().parseCaseInsensitive().append(DateTimeFormatter.ISO_LOCAL_DATE).appendLiteral('T').append(DateTimeFormatter.ISO_LOCAL_TIME).toFormatter();
33618 tejus.loha 334
 
335
        DateTimeFormatter dateFormatter = new DateTimeFormatterBuilder().parseCaseInsensitive().append(DateTimeFormatter.ISO_LOCAL_DATE).toFormatter();
32054 tejbeer 336
        JavaTimeModule jtm = new JavaTimeModule();
33618 tejus.loha 337
        jtm.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(sf));
338
        jtm.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(df));
339
        jtm.addSerializer(LocalDate.class, new LocalDateSerializer(dateFormatter));
340
        jtm.addDeserializer(LocalDate.class, new LocalDateDeserializer(dateFormatter));
32569 amit.gupta 341
        SimpleModule stringModule = new SimpleModule("String trimmer deserialize module");
342
        stringModule.addDeserializer(String.class, new CustomStringDeserializer());
32186 amit.gupta 343
        ObjectMapper mapper = new ObjectMapper()
32187 amit.gupta 344
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
32569 amit.gupta 345
                .registerModule(new ParameterNamesModule()).registerModule(new Jdk8Module()).registerModule(jtm)
346
                .registerModule(stringModule); // new module, NOT JSR310Module
32054 tejbeer 347
        return mapper;
348
    }
31282 amit.gupta 349
 
37072 amit 350
    class CustomStringDeserializer extends StringDeserializer implements ContextualDeserializer {
351
 
352
        // Fields whose exact bytes matter — never normalize (credentials, tokens, payloads).
353
        private final java.util.regex.Pattern SENSITIVE_FIELD =
354
                java.util.regex.Pattern.compile("(?i)(password|passwd|pwd|otp|pin|token|secret|signature|hash|base64|apikey|api_key)");
355
 
356
        private final boolean normalize;
357
 
358
        CustomStringDeserializer() {
359
            this(true);
360
        }
361
 
362
        CustomStringDeserializer(boolean normalize) {
363
            this.normalize = normalize;
364
        }
365
 
32569 amit.gupta 366
        @Override
37072 amit 367
        public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) {
368
            if (property != null && SENSITIVE_FIELD.matcher(property.getName()).find()) {
369
                return new CustomStringDeserializer(false);
370
            }
371
            return this;
372
        }
373
 
374
        @Override
32569 amit.gupta 375
        public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
376
            String text = super.deserialize(p, ctxt);
37072 amit 377
            if (!normalize) {
378
                return text;
379
            }
380
            // trim + NBSP/tab/zero-width -> single space + collapse doubles
381
            return com.spice.profitmandi.common.util.StringUtils.normalizeWhitespace(text);
32569 amit.gupta 382
        }
383
    }
384
 
385
 
21555 kshitij.so 386
}