diff --git a/frontend/src/app/services/enterprise.service.ts b/frontend/src/app/services/enterprise.service.ts index d7695b94f..5f194c77f 100644 --- a/frontend/src/app/services/enterprise.service.ts +++ b/frontend/src/app/services/enterprise.service.ts @@ -51,6 +51,7 @@ export class EnterpriseService { if (this.stateService.env.customize?.branding) { const info = this.stateService.env.customize?.branding; this.insertMatomo(info.site_id); + this.seoService.setCustomMeta(this.stateService.env.customize); this.seoService.setEnterpriseTitle(info.title, true); this.info$.next(info); } else { diff --git a/frontend/src/app/services/opengraph.service.ts b/frontend/src/app/services/opengraph.service.ts index e969dd07a..03b183d15 100644 --- a/frontend/src/app/services/opengraph.service.ts +++ b/frontend/src/app/services/opengraph.service.ts @@ -12,6 +12,9 @@ import { LanguageService } from '@app/services/language.service'; export class OpenGraphService { network = ''; defaultImageUrl = ''; + defaultImageType = 'image/png'; + defaultImageWidth = '1000'; + defaultImageHeight = '500'; previewLoadingEvents = {}; previewLoadingCount = 0; @@ -25,12 +28,17 @@ export class OpenGraphService { ) { // save og:image tag from original template const initialOgImageTag = metaService.getTag("property='og:image'"); - this.defaultImageUrl = initialOgImageTag?.content || 'https://mempool.space/resources/previews/mempool-space-preview.jpg'; + this.defaultImageUrl = (this.stateService.env.customize?.meta?.image?.src ? this.stateService.env.customize.meta.image.src : initialOgImageTag?.content) || 'https://mempool.space/resources/previews/mempool-space-preview.jpg'; + this.defaultImageType = (this.stateService.env.customize?.meta?.image?.type ? this.stateService.env.customize.meta.image.type : 'image/png'); + this.defaultImageWidth = (this.stateService.env.customize?.meta?.image?.width ? this.stateService.env.customize.meta.image.width : '1000'); + this.defaultImageHeight = (this.stateService.env.customize?.meta?.image?.height ? this.stateService.env.customize.meta.image.height : '500'); this.router.events.pipe( filter(event => event instanceof NavigationEnd), map(() => this.activatedRoute), map(route => { - while (route.firstChild) route = route.firstChild; + while (route.firstChild) { + route = route.firstChild; + } return route; }), filter(route => route.outlet === 'primary'), @@ -45,7 +53,7 @@ export class OpenGraphService { // expose routing method to global scope, so we can access it from the unfurler window['ogService'] = { - loadPage: (path) => { return this.loadPage(path) } + loadPage: (path) => { return this.loadPage(path); } }; } @@ -62,9 +70,9 @@ export class OpenGraphService { clearOgImage() { this.metaService.updateTag({ property: 'og:image', content: this.defaultImageUrl }); this.metaService.updateTag({ name: 'twitter:image', content: this.defaultImageUrl }); - this.metaService.updateTag({ property: 'og:image:type', content: 'image/png' }); - this.metaService.updateTag({ property: 'og:image:width', content: '1000' }); - this.metaService.updateTag({ property: 'og:image:height', content: '500' }); + this.metaService.updateTag({ property: 'og:image:type', content: this.defaultImageType }); + this.metaService.updateTag({ property: 'og:image:width', content: this.defaultImageWidth }); + this.metaService.updateTag({ property: 'og:image:height', content: this.defaultImageHeight }); } setManualOgImage(imageFilename) { diff --git a/frontend/src/app/services/seo.service.ts b/frontend/src/app/services/seo.service.ts index e5ede4db3..97392edde 100644 --- a/frontend/src/app/services/seo.service.ts +++ b/frontend/src/app/services/seo.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { Title, Meta } from '@angular/platform-browser'; import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; import { filter, map, switchMap } from 'rxjs'; -import { StateService } from '@app/services/state.service'; +import { Customization, StateService } from '@app/services/state.service'; @Injectable({ providedIn: 'root' @@ -23,13 +23,17 @@ export class SeoService { private activatedRoute: ActivatedRoute, ) { // save original meta tags - this.baseDescription = metaService.getTag('name=\'description\'')?.content || this.baseDescription; - this.baseTitle = titleService.getTitle()?.split(' - ')?.[0] || this.baseTitle; - try { - const canonicalUrl = new URL(this.canonicalLink?.href || ''); - this.baseDomain = canonicalUrl?.host; - } catch (e) { - // leave as default + this.baseDescription = this.stateService.env.customize?.meta?.description || metaService.getTag('name=\'description\'')?.content || this.baseDescription; + this.baseTitle = this.stateService.env.customize?.meta?.title || titleService.getTitle()?.split(' - ')?.[0] || this.baseTitle; + if (this.stateService.env.customize?.domains?.length) { + this.baseDomain = this.stateService.env.customize.domains[0]; + } else { + try { + const canonicalUrl = new URL(this.canonicalLink?.href || ''); + this.baseDomain = canonicalUrl?.host; + } catch (e) { + // leave as default + } } this.stateService.networkChanged$.subscribe((network) => this.network = network); @@ -70,6 +74,22 @@ export class SeoService { this.resetTitle(); } + setCustomMeta(customize: Customization) { + if (!customize.meta) { + return; + } + this.metaService.updateTag({ name: 'description', content: customize.meta.description}); + this.metaService.updateTag({ name: 'twitter:description', content: customize.meta.description}); + this.metaService.updateTag({ property: 'og:description', content: customize.meta.description}); + this.metaService.updateTag({ name: 'twitter:image', content: customize.meta.image.src}); + this.metaService.updateTag({ property: 'og:image', content: customize.meta.image.src}); + this.metaService.updateTag({ property: 'og:image:type', content: customize.meta.image.type}); + this.metaService.updateTag({ property: 'og:image:width', content: customize.meta.image.width}); + this.metaService.updateTag({ property: 'og:image:height', content: customize.meta.image.height}); + const domain = customize.domains?.[0] || window.location.hostname; + this.metaService.updateTag({ name: 'twitter:domain', content: domain}); + } + setDescription(newDescription: string): void { this.metaService.updateTag({ name: 'description', content: newDescription}); this.metaService.updateTag({ name: 'twitter:description', content: newDescription}); diff --git a/frontend/src/app/services/state.service.ts b/frontend/src/app/services/state.service.ts index 0d006b552..6dc1786bc 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -22,6 +22,7 @@ export interface MarkBlockState { export interface ILoadingIndicators { [name: string]: number; } export interface Customization { + domains: string[]; theme: string; enterprise?: string; branding: { @@ -33,6 +34,16 @@ export interface Customization { footer_img?: string; rounded_corner: boolean; }, + meta: { + title: string; + description: string; + image: { + src: string; + type: string; + width: string; + height: string; + }; + }; dashboard: { widgets: { component: string;