CtanConfiguration.java

/*
 * Copyright © 2022-2025 The CTAN Team and individual authors
 *
 * This file is distributed under the 3-clause BSD license.
 * See file LICENSE for details.
 */
package org.ctan.site;

import java.util.List;
import java.util.Map;

import org.ctan.site.services.util.NullCheck;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.dropwizard.core.Configuration;
import io.dropwizard.db.DataSourceFactory;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Builder.Default;
import lombok.Getter;
import lombok.NoArgsConstructor;

/**
 * The class <code>CtanConfiguration</code> contains the Dropwizard
 * configuration specific to the CTAN site.
 *
 * <p>
 * The configuration is externally stored in a yaml file and read at startup.
 * </p>
 *
 * <p>
 * The configuration might contain references to environment variables. This can
 * be achieved with a construct like <code>${VARIABLE}</code>. This takes the
 * value of VARIABLE from the environment. If the variable might not be defined
 * then <code>${VARIABLE:-FALLBACK}</code> can be used to revert to FALLBACK in
 * this case.
 * </p>
 *
 * <p>
 * The following example demonstrates the major configuration parameters.
 * </p>
 * <pre>
 * appName: Comprehensive TeX Archive Network
 * version: 3.0.0
 *
 * #---------------------------------------------------------------------------
 * # Server settings.
 * server:
 *   rootPath: '/api/*'
 *   applicationConnectors:
 *     - type: http
 *       port: 9000
 *   adminConnectors:
 *     - type: http
 *       port: 9001
 *
 * #---------------------------------------------------------------------------
 * # CTAN settings.
 * ctan:
 *   defaultLanguage: en
 *   languages:
 *     - en
 *     - de
 *
 * #---------------------------------------------------------------------------
 * # CTAN searching settings.
 * index:
 *   directory: /serv/www/www.ctan.org/index
 *
 * #---------------------------------------------------------------------------
 * # CTAN content settings.
 * content:
 *   directory: /serv/www/www.ctan.org/src/ctan-content
 *
 * #---------------------------------------------------------------------------
 * # CTAN incoming settings.
 * upload:
 *   addendum: /home/ftp/pub/tex/help/ctan/CTAN-upload-addendum.html
 *   incoming: /serv/www/www.ctan.org/incoming
 *   managers: ctan@ctan.org
 *
 * #---------------------------------------------------------------------------
 * # CTAN TeX Catalogue settings.
 * catalogue:
 *   entries: /serv/www/www.ctan.org/texcatalogue/entries
 *
 * #---------------------------------------------------------------------------
 * # CTAN tex-archive settings.
 * texArchive:
 *   directory: /home/ftp/pub/tex
 *
 * #---------------------------------------------------------------------------
 * # CTAN ctan-ann settings.
 * ctanAnnounce:
 *   directory: /serv/www/www.ctan.org/ctan-ann
 *
 * #---------------------------------------------------------------------------
 * # MirrMon settings.
 * mirrmon:
 *   url: https://ctan.org/mirmon
 *
 * #---------------------------------------------------------------------------
 * # LUGs database settings.
 * lugs:
 *   url: https://www.ntg.nl/lug/lugs/
 *
 * #---------------------------------------------------------------------------
 * # Mail settings.
 * mail:
 *   smtp:
 *     host: localhost
 *     port: 587
 *   from: no-reply@ctan.org
 *     list:
 *       mirror-registration:
 *         template: mirror-registration
 *         to: ctan@ctan.org
 *
 * #---------------------------------------------------------------------------
 * # Database settings.
 * site_db:
 *   driverClass: org.postgresql.Driver
 *   user: ${SITE_DB_USER:-www}
 *   password: ${SITE_DB_PASSWD}
 *   url: jdbc:postgresql://localhost:5432/${SITE_DB:-portal}
 *   properties:
 *     charSet: UTF-8
 *     hibernate.dialect: org.hibernate.dialect.PostgreSQLDialect
 *   maxWaitForConnection: 1s
 *   validationQuery: "SELECT 1"
 *   minSize: 8
 *   maxSize: 32
 *   checkConnectionWhileIdle: false
 *
 * mirrors:
 *   driverClass: org.postgresql.Driver
 *   user: ${MIRRORS_DB_USER:-www}
 *   password: ${MIRRORS_DB_PASSWD}
 *   url: jdbc:postgresql://localhost:5432/${MIRRORS_DB:-ctan}
 *   properties:
 *     charSet: UTF-8
 *     hibernate.dialect: org.hibernate.dialect.PostgreSQLDialect
 *   maxWaitForConnection: 1s
 *   validationQuery: "SELECT 1"
 *   minSize: 8
 *   maxSize: 32
 *   checkConnectionWhileIdle: false
 * </pre>
 *
 * @author <a href="mailto:gene@ctan.org">Gerd Neugebauer</a>
 */
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Builder
@SuppressFBWarnings(value = "EI_EXPOSE_REP")
public class CtanConfiguration extends Configuration {

    /**
     * The class <code>CatalogueConfig</code> contains the Dropwizard
     * configuration for the Catalogue.
     */
    @Getter
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public static class CatalogueConfig {

        /**
         * The field <code>entries</code> contains the name of the entries
         * subdirectory.
         */
        private String entries;
    }

    /**
     * The class <code>ContentConfig</code> contains the definition of the
     * sub-configuration <code>content</code>.
     */
    @Getter
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public static class ContentConfig {

        /**
         * The field <code>directory</code> contains the path of the content
         * directory.
         */
        private String directory;
    }

    /**
     * The class <code>CtanAnnounceConfig</code> contains the definition of the
     * sub-configuration <code>ctanAnnounce</code>.
     */
    @Getter
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public static class CtanAnnounceConfig {

        /**
         * The field <code>directory</code> contains the directory containing
         * the announcements.
         */
        private String directory;
    }

    /**
     * The class <code>CtanParams</code> contains the description of the section
     * <code>ctan</code> of the configuration.
     */
    @Getter
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public static class CtanConfig {

        /**
         * The field <code>LOCALES</code> contains the supported locales in a
         * static list. Note that this may conflict with the configuration
         * option. BE CAREFUL!
         */
        public static final List<String> LOCALES = List.of("en", "de");

        /**
         * The field <code>languages</code> contains the list of supported
         * languages.
         */
        private String[] languages;
    }

    /**
     * The class <code>IndexConfig</code> contains the definition of the
     * sub-configuration <code>index</code>.
     */
    @Getter
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public static class IndexConfig {

        /**
         * The field <code>directory</code> contains the path of the index
         * master directory.
         */
        private String directory;
    }

    /**
     * The class <code>LugsConfig</code> contains the Dropwizard configuration
     * for the Lugs.
     */
    @Getter
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public static class LugsConfig {

        /**
         * The field <code>url</code> contains the URL to retrieve the LUGs
         * data.
         */
        private String url;
    }

    /**
     * The mail configuration.
     */
    @Getter
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public static class MailConfig {

        /**
         * The field <code>smtp</code> contains the SMTP configuration.
         */
        private MailSmtpConfig smtp;

        /**
         * The field <code>from</code> contains the sender email address.
         */
        private String from;

        /**
         * The field <code>list</code> contains the map of name to mail list
         * data.
         */
        private Map<String, MailType> list;

        /**
         * The method <code>getList</code> provides means to retrieve the
         * configuration for one mail list.
         *
         * @param key the name of the mail list
         * @return the mail list
         */
        public MailType getList(String key) {

            NullCheck.isNotNullObject(list, "mail.list");
            return list.get(key);
        }
    }

    /**
     * The SMTP configuration.
     */
    @Getter
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public static class MailSmtpConfig {

        /**
         * The field <code>host</code> contains the SMTP host.
         */
        private String host;

        /**
         * The field <code>port</code> contains the SMTP port as String.
         */
        private String port;

        /**
         * The field <code>auth</code> contains the indicator whether
         * authentication is required.
         */
        private boolean auth;

        /**
         * The field <code>starttls</code> contains the SMTP starttls
         * configuration.
         */
        private MailSmtpStarttls starttls;
    }

    /**
     * The SMTP starttls configuration.
     */
    @Getter
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public static class MailSmtpStarttls {

        /**
         * The field <code>enable</code> contains the indicator to turn on
         * STARTTLS.
         */
        private boolean enable;
    }

    /**
     * The mail list type configuration.
     */
    @Getter
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public static class MailType {

        /**
         * The field <code>template</code> contains the name of the template to
         * use.
         */
        private String template;

        /**
         * The field <code>to</code> contains the addressee for the mails to
         * this list.
         */
        private String to;
    }

    /**
     * The class <code>MirrMonConfig</code> contains the Dropwizard
     * configuration for MirrMon.
     */
    @Getter
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public static class MirrMonConfig {

        /**
         * The field <code>url</code> contains the source of the mirmon page.
         */
        private String url;
    }

    /**
     * The class <code>TexArchiveConfig</code> contains the definition of the
     * sub-configuration <code>texArchive</code>.
     */
    @Getter
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public static class TexArchiveConfig {

        /**
         * The field <code>directory</code> contains the <span>T<span style=
         * "text-transform:uppercase;font-size:90%;vertical-align:-0.4ex;
         * margin-left:-0.2em;margin-right:-0.1em;line-height: 0;" >e</span>
         * X</span> archive directory.
         */
        private String directory;
    }

    /**
     * The class <code>UploadConfig</code> contains the definition of the
     * sub-configuration <code>upload</code>.
     */
    @Getter
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public static class UploadConfig {

        /**
         * The field <code>addendum</code> contains the name of the addendum
         * file.
         */
        private String addendum;

        /**
         * The field <code>incoming</code> contains the path of the incoming
         * directory.
         */
        private String incoming;

        /**
         * The field <code>managers</code> contains the email address for
         * informing the CTAN upload managers about a new upload.
         */
        private String managers;
    }

    /**
     * The field <code>appName</code> contains the name of the application.
     */
    private String appName;

    /**
     * The field <code>content</code> contains the base directory for the
     * content tree.
     */
    private ContentConfig content;

    /**
     * The field <code>ctan</code> contains the CTAN parameters.
     */
    private CtanConfig ctan;

    /**
     * The field <code>ctanAnnounce</code> contains the parameters for ctan-ann.
     */
    private CtanAnnounceConfig ctanAnnounce;

    /**
     * The field <code>lugs</code> contains the parameter for lugs.
     */
    private LugsConfig lugs;

    /**
     * The field <code>mirrMon</code> contains the parameter for MirrMon.
     */
    private MirrMonConfig mirrmon;

    /**
     * The field <code>index</code> contains the base directory for the search
     * indices.
     */
    private IndexConfig index;

    /**
     * The field <code>mail</code> contains configuration for mail.
     */
    private MailConfig mail;

    /**
     * The field <code>mirrors</code> contains the mirrors database.
     */
    @NotNull
    @Valid
    @Default
    private DataSourceFactory mirrors = new DataSourceFactory();

    /**
     * The field <code>siteDb</code> contains the site database.
     */
    @NotNull
    @Valid
    @Default
    private DataSourceFactory siteDb = new DataSourceFactory();

    /**
     * The field <code>texArchive</code> contains the base directory for the TeX
     * archive.
     */
    private TexArchiveConfig texArchive;

    /**
     * The field <code>upload</code> contains the incoming directory.
     */
    private UploadConfig upload;

    /**
     * The field <code>catalogue</code> contains the catalogue entries
     * directory.
     */
    private CatalogueConfig catalogue;

    /**
     * The field <code>version</code> contains the version of the application.
     */
    private String version;
}