Как создать несколько динамических тематических приложений Angular с помощью всего одного развертывания

Прежде всего: извините за неуклюжее название, но я не мог придумать ничего лучше. Итак, давайте начнем с объяснения того, что я имею в виду 🤓:

Мы создали для одного из наших клиентов Angular PWA для. Это приложение предназначалось для того, чтобы пользователи могли заказывать такси в дороге. Бизнес нашего клиента заключается в передаче этих заказов таксомоторным компаниям и взимании платы за эту услугу.

Затем некоторые из таксомоторных компаний спросили нашего клиента, могут ли они использовать приложение в другом домене с их собственным брендом. Они хотели рекламировать «свое» приложение в своем регионе, не опасаясь продвижения другого конкурента.

Представьте, что вы спрашиваете Uber, можете ли вы взять их приложение и переименовать его с вашим собственным именем, с вашим логотипом и цветами, но Uber по-прежнему занимается обработкой заказов, онлайн-платежами и т. Д.

Итак, как этого добиться с минимальными усилиями? 🤔

Если вы хотите настроить PWA, вы можете индивидуализировать несколько тем:

  • Стили CSS, например цвета, семейство шрифтов, размеры, логотипы и т. д.
  • Файл manifest.webmanifest, содержащий имена и значки, когда пользователь добавляет приложение на рабочий стол.
  • У вас есть теги <title /> и <meta />, которые вы хотите индивидуализировать.
  • Статическая информация о странице, например выходные данные и условия конфиденциальности.

Кроме того, есть еще несколько организационных вопросов, на которые вы должны ответить себе:

  • Как развернуть несколько приложений одновременно при обновлении чего-либо в кодовой базе?
  • Как гарантировать, что все приложения работают, даже если у некоторых клиентов есть особые конфигурации?
  • Как отслеживать все индивидуализированные версии приложений.

Наша главная цель заключалась в том, чтобы свести к минимуму объем работы, необходимой для развертывания другой фирменной версии приложения.

Что мы сделали для решения этих проблем? ⭐️

Приложение было развернуто в кластере Kubernetes с контроллером Ingress на передней панели. В качестве предпосылки мы предполагаем, что ваш PWA уже развернут и доступен под доменным именем.

Шаг 1

Итак, шаг 1 заключался в том, чтобы просто добавить еще один домен к Ingress. Таким образом, приложение было доступно не только под example.com, но и с company-one.com. Этот шаг не должен требовать каких-либо дополнительных настроек внутри приложения Angular.

Шаг 2

Для шага 2 мы добавили новый APP_INITIALIZER к основному AppModule следующим образом:

# app.module.ts
export function provideAppConfig(configService: ConfigService) {
    return (): Promise<any> => {
        return configService.init();
    };
}
[...]
providers: [
    {provide: APP_INITIALIZER, useFactory: provideAppConfig, deps: [ConfigService], multi: true},
    ...
]

ConfigService выглядит так:

# config.service.ts
@Injectable({
    providedIn: 'root'
})
export class ConfigService {
[...]
    init() {
        this.http.get(`your-api.com/get-config`)
        .subscribe(config => {
            
            THIS PART WILL BE EXPLAINED LATER 😉
        })
    }
}

Таким образом мы гарантируем, что приложение загрузит конфигурацию до инициализации приложения Angular.

Теперь вам интересно, как сервер отвечает?

На сервере смотрим на заголовокOrigin. ❗️ Это основная тема всей статьи. ❗️ Мы получаем конфигурацию, основанную на заголовке источника, например company-one.com, из базы данных и возвращаем его. Конфигурация зависит от ваших потребностей. Для нас это в основном цвета, URL-адрес логотипа, заголовок страницы и настройки некоторых приложений. Вот наш конфиг в качестве примера:

{
    "origin" : "https://company-one.com",
    "title" : "Super awesome app",
    "icons" : [ 
        {
            "type" : "image/png",
            "rel" : "apple-touch-icon",
            "href" : "/assets/icons/apple-touch-icon.png",
            "sizes" : "180x180"
        }, 
        {
            "type" : "image/x-icon",
            "rel" : "icon",
            "href" : "/assets/icons/favicon.ico"
        }, 
        {
            "type" : "image/png",
            "rel" : "icon",
            "href" : "/assets/icons/favicon-16x16.png",
            "sizes" : "16x16"
        }, 
        {
            "type" : "image/png",
            "rel" : "icon",
            "href" : "/assets/icons/favicon-32x32.png",
            "sizes" : "32x32"
        }
    ],
    "manifest" : {
        "name" : "Company's A Taxi App",
        "short_name" : "Company's A",
        "display" : "standalone",
        "scope" : "/",
        "start_url" : "/",
        "theme_color" : "#4F5051",
        "background_color" : "#4F5051",
        "icons" : [ 
            {
                "src" : "https://company-one.com/assets/icons/icon-72x72.png",
                "sizes" : "72x72",
                "type" : "image/png"
            }, 
            {
                "src" : "http://company-one.com/assets/icons/icon-96x96.png",
                "sizes" : "96x96",
                "type" : "image/png"
            }, 
            {
                "src" : "http://company-one.com/assets/icons/icon-128x128.png",
                "sizes" : "128x128",
                "type" : "image/png"
            }, 
            {
                "src" : "http://company-one.com/assets/icons/icon-144x144.png",
                "sizes" : "144x144",
                "type" : "image/png"
            }, 
            {
                "src" : "https://company-one.com/assets/icons/icon-152x152.png",
                "sizes" : "152x152",
                "type" : "image/png"
            }, 
            {
                "src" : "https://company-one.com/assets/icons/icon-192x192.png",
                "sizes" : "192x192",
                "type" : "image/png"
            }, 
            {
                "src" : "https://company-one.com/assets/icons/icon-384x384.png",
                "sizes" : "384x384",
                "type" : "image/png"
            }, 
            {
                "src" : "https://company-one.com/assets/icons/icon-512x512.png",
                "sizes" : "512x512",
                "type" : "image/png"
            }
        ]
    },
    "variables" : {
        "default-btn-color" : "#212529",
        "default-btn-background" : "#ffcb03",
        "default-btn-border" : "#ffcb03",
        "default-btn-active-color" : "#212529",
        "default-btn-active-background" : "#cfa400",
        "default-btn-active-border" : "#c29a00",
        "default-btn-hover-color" : "#212529",
        "default-btn-hover-background" : "#cfa400",
        "default-btn-hover-border" : "#cfa400",
        "splash-screen-background-color" : "#4F5051",
        "splash-screen-text-color" : "#E1E1E1",
        "splashScreenIconUrl" : "/assets/icons/icon-512x512.png",
        "splashScreenLogoUrl" : "/assets/svg/logo_inverted.svg",
        "logoUrl" : "/assets/svg/logo.svg"
    }
}

Шаг 3

В методе init() мы обрабатываем конфигурацию и заботимся об индивидуализации стиля, упомянутой ранее. Например, мы хотим добавить значки и цвет темы:

# config json
{
"manifest": {
    "theme_color": "#FF0000",
},
"icons" : [ 
        {
            "type" : "image/png",
            "rel" : "apple-touch-icon",
            "href" : "asset-url.com/apple-touch-icon.png",
            "sizes" : "180x180"
        },
...
]
}
# ConfigService.init()
// Add icons
config.icons.forEach(icon => {
    const l: HTMLLinkElement = this.document.createElement('link');
    Object.entries(icon).forEach(([key, value]) => {
        l[key] = value;
    });
    this.document.head.appendChild(l);
});
// Add theme-color
const m: HTMLMetaElement = this.document.createElement('meta');
m.name = 'theme-color';
m.content = config.manifest.theme_color;
this.document.head.appendChild(m);

Далее настраиваем укладку. Для этого мы используем переменные CSS. Сначала мы добавляем их в контекст тела:

Object.entries(res.variables).forEach(([key, value]) => {
    this.document.body.style.setProperty('--' + key, value.toString());
});

А затем вы можете просто использовать их где угодно в своем коде SCSS:

color: var(--text-color, #666); // Use --text-color or #666 as default if --text-color is not set

В настоящее время переменные CSS хорошо поддерживаются всеми основными поставщиками браузеров, поэтому, на мой взгляд, их можно безопасно использовать. Только не забудьте добавить общие резервные цвета для старого доброго IE.

Шаг 4

Вы добавляете запись manifest.webmanifest в свой <head />. Но вместо того, чтобы указывать на реальный файл на вашем сервере, вы указываете на конечную точку, которая динамически создает информацию манифеста, также на основе исходного заголовка. Для этого вам не нужны особые требования, вы можете делать это независимо от того, как выглядит ваша серверная часть. Просто убедитесь, что ваш сервер не самый медленный, иначе могут возникнуть проблемы с некоторыми браузерами, обнаруживающими файл манифеста.

Шаг 5

Мы также используем автономную CMS для доставки контента, такого как тексты для SEO, выходные данные и условия конфиденциальности. Поэтому мы добавили в конфиг ключ api, в котором можно найти тексты.

ШАГ 6

Помимо конфигураций стилей и именования, мы также добавили некоторую информацию, связанную с бизнесом, о районе, где компания предлагает свои услуги, цены и предложения. Но это может варьироваться в зависимости от того, какое приложение вы разрабатываете.

Заключение 💎

С минимальными усилиями мы создали полностью настраиваемую PWA и можем добавлять новых клиентов в приложение, не затрагивая процесс развертывания. Все клиенты всегда в курсе, и в нашем случае мы также можем получить выгоду от перекрестных продаж: если один пользователь хочет заказать такси в районе, где компания приложения, которое он использует, не представлена, мы можем показать ему альтернативы, не нанося вреда любой бизнес и не заставляя пользователя устанавливать другое приложение.

Вам просто нужно воспользоваться заголовком, который автоматически добавляется браузером, и до сих пор вы не обращали на это особого внимания 🤘🏼.

Если вас интересуют другие статьи, связанные с Angular или PWA, попробуйте эту:



📝 Сохраните этот рассказ в Журнале.

👩‍💻 Просыпайтесь каждое воскресное утро и слышите самые интересные истории недели в области технологий, ожидающие в вашем почтовом ящике. Прочтите информационный бюллетень« Примечательные в технологиях ».