diff --git a/frontend/src/app/bisq/bisq-address/bisq-address.component.ts b/frontend/src/app/bisq/bisq-address/bisq-address.component.ts index eccc88bc7..7711c4d3c 100644 --- a/frontend/src/app/bisq/bisq-address/bisq-address.component.ts +++ b/frontend/src/app/bisq/bisq-address/bisq-address.component.ts @@ -47,6 +47,7 @@ export class BisqAddressComponent implements OnInit, OnDestroy { catchError((err) => { this.isLoadingAddress = false; this.error = err; + this.seoService.logSoft404(); console.log(err); return of(null); }) @@ -62,6 +63,7 @@ export class BisqAddressComponent implements OnInit, OnDestroy { (error) => { console.log(error); this.error = error; + this.seoService.logSoft404(); this.isLoadingAddress = false; }); } diff --git a/frontend/src/app/bisq/bisq-block/bisq-block.component.ts b/frontend/src/app/bisq/bisq-block/bisq-block.component.ts index 1bb3a24ab..206a18031 100644 --- a/frontend/src/app/bisq/bisq-block/bisq-block.component.ts +++ b/frontend/src/app/bisq/bisq-block/bisq-block.component.ts @@ -82,6 +82,7 @@ export class BisqBlockComponent implements OnInit, OnDestroy { ) .subscribe((block: BisqBlock) => { if (!block) { + this.seoService.logSoft404(); return; } this.isLoading = false; @@ -97,6 +98,7 @@ export class BisqBlockComponent implements OnInit, OnDestroy { caughtHttpError(err: HttpErrorResponse){ this.error = err; + this.seoService.logSoft404(); return of(null); } } diff --git a/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.ts b/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.ts index fb30fc59f..78e2c0bb0 100644 --- a/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.ts +++ b/frontend/src/app/bisq/bisq-transaction/bisq-transaction.component.ts @@ -70,11 +70,13 @@ export class BisqTransactionComponent implements OnInit, OnDestroy { catchError((txError: HttpErrorResponse) => { console.log(txError); this.error = txError; + this.seoService.logSoft404(); return of(null); }) ); } this.error = bisqTxError; + this.seoService.logSoft404(); return of(null); }) ); @@ -103,6 +105,7 @@ export class BisqTransactionComponent implements OnInit, OnDestroy { this.isLoadingTx = false; if (!tx) { + this.seoService.logSoft404(); return; } diff --git a/frontend/src/app/components/address/address.component.ts b/frontend/src/app/components/address/address.component.ts index 2ae9a962b..b7752228d 100644 --- a/frontend/src/app/components/address/address.component.ts +++ b/frontend/src/app/components/address/address.component.ts @@ -88,6 +88,7 @@ export class AddressComponent implements OnInit, OnDestroy { catchError((err) => { this.isLoadingAddress = false; this.error = err; + this.seoService.logSoft404(); console.log(err); return of(null); }) @@ -157,6 +158,7 @@ export class AddressComponent implements OnInit, OnDestroy { (error) => { console.log(error); this.error = error; + this.seoService.logSoft404(); this.isLoadingAddress = false; }); diff --git a/frontend/src/app/components/asset/asset.component.ts b/frontend/src/app/components/asset/asset.component.ts index 0e642063a..562ebff53 100644 --- a/frontend/src/app/components/asset/asset.component.ts +++ b/frontend/src/app/components/asset/asset.component.ts @@ -86,6 +86,7 @@ export class AssetComponent implements OnInit, OnDestroy { catchError((err) => { this.isLoadingAsset = false; this.error = err; + this.seoService.logSoft404(); console.log(err); return of(null); }) @@ -153,6 +154,7 @@ export class AssetComponent implements OnInit, OnDestroy { (error) => { console.log(error); this.error = error; + this.seoService.logSoft404(); this.isLoadingAsset = false; }); diff --git a/frontend/src/app/components/block/block-preview.component.ts b/frontend/src/app/components/block/block-preview.component.ts index d0fec960a..7c10dab6f 100644 --- a/frontend/src/app/components/block/block-preview.component.ts +++ b/frontend/src/app/components/block/block-preview.component.ts @@ -82,6 +82,7 @@ export class BlockPreviewComponent implements OnInit, OnDestroy { }), catchError((err) => { this.error = err; + this.seoService.logSoft404(); this.openGraphService.fail('block-data-' + this.rawId); this.openGraphService.fail('block-viz-' + this.rawId); return of(null); @@ -138,6 +139,7 @@ export class BlockPreviewComponent implements OnInit, OnDestroy { (error) => { this.error = error; this.isLoadingOverview = false; + this.seoService.logSoft404(); this.openGraphService.fail('block-viz-' + this.rawId); this.openGraphService.fail('block-data-' + this.rawId); if (this.blockGraph) { diff --git a/frontend/src/app/components/block/block.component.ts b/frontend/src/app/components/block/block.component.ts index f5a0c93b0..6494d2f70 100644 --- a/frontend/src/app/components/block/block.component.ts +++ b/frontend/src/app/components/block/block.component.ts @@ -192,6 +192,7 @@ export class BlockComponent implements OnInit, OnDestroy { this.error = err; this.isLoadingBlock = false; this.isLoadingOverview = false; + this.seoService.logSoft404(); return EMPTY; }) ); @@ -200,6 +201,7 @@ export class BlockComponent implements OnInit, OnDestroy { this.error = err; this.isLoadingBlock = false; this.isLoadingOverview = false; + this.seoService.logSoft404(); return EMPTY; }), ); @@ -215,6 +217,7 @@ export class BlockComponent implements OnInit, OnDestroy { this.error = err; this.isLoadingBlock = false; this.isLoadingOverview = false; + this.seoService.logSoft404(); return EMPTY; }) ); diff --git a/frontend/src/app/components/pool/pool-preview.component.ts b/frontend/src/app/components/pool/pool-preview.component.ts index 277bacb33..aa83537fe 100644 --- a/frontend/src/app/components/pool/pool-preview.component.ts +++ b/frontend/src/app/components/pool/pool-preview.component.ts @@ -61,6 +61,7 @@ export class PoolPreviewComponent implements OnInit { }), catchError(() => { this.isLoading = false; + this.seoService.logSoft404(); this.openGraphService.fail('pool-hash-' + this.slug); return of([slug]); }) @@ -70,6 +71,7 @@ export class PoolPreviewComponent implements OnInit { return this.apiService.getPoolStats$(slug).pipe( catchError(() => { this.isLoading = false; + this.seoService.logSoft404(); this.openGraphService.fail('pool-stats-' + this.slug); return of(null); }) diff --git a/frontend/src/app/components/pool/pool.component.ts b/frontend/src/app/components/pool/pool.component.ts index 56b8bd392..1c2ce5c1f 100644 --- a/frontend/src/app/components/pool/pool.component.ts +++ b/frontend/src/app/components/pool/pool.component.ts @@ -1,8 +1,8 @@ import { ChangeDetectionStrategy, Component, Inject, Input, LOCALE_ID, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { EChartsOption, graphic } from 'echarts'; -import { BehaviorSubject, Observable, timer } from 'rxjs'; -import { distinctUntilChanged, map, share, switchMap, tap } from 'rxjs/operators'; +import { BehaviorSubject, Observable, of, timer } from 'rxjs'; +import { catchError, distinctUntilChanged, map, share, switchMap, tap } from 'rxjs/operators'; import { BlockExtended, PoolStat } from '../../interfaces/node-api.interface'; import { ApiService } from '../../services/api.service'; import { StateService } from '../../services/state.service'; @@ -59,10 +59,21 @@ export class PoolComponent implements OnInit { this.prepareChartOptions(data.map(val => [val.timestamp * 1000, val.avgHashrate])); return [slug]; }), + catchError(() => { + this.isLoading = false; + this.seoService.logSoft404(); + return of([slug]); + }) ); }), switchMap((slug) => { - return this.apiService.getPoolStats$(slug); + return this.apiService.getPoolStats$(slug).pipe( + catchError(() => { + this.isLoading = false; + this.seoService.logSoft404(); + return of(null); + }) + ); }), tap(() => { this.loadMoreSubject.next(this.blocks[this.blocks.length - 1]?.height); diff --git a/frontend/src/app/components/transaction/transaction-preview.component.ts b/frontend/src/app/components/transaction/transaction-preview.component.ts index 6db0e588c..07843cc57 100644 --- a/frontend/src/app/components/transaction/transaction-preview.component.ts +++ b/frontend/src/app/components/transaction/transaction-preview.component.ts @@ -133,6 +133,7 @@ export class TransactionPreviewComponent implements OnInit, OnDestroy { ) .subscribe((tx: Transaction) => { if (!tx) { + this.seoService.logSoft404(); this.openGraphService.fail('tx-data-' + this.txId); return; } @@ -182,6 +183,7 @@ export class TransactionPreviewComponent implements OnInit, OnDestroy { this.openGraphService.waitOver('tx-data-' + this.txId); }, (error) => { + this.seoService.logSoft404(); this.openGraphService.fail('tx-data-' + this.txId); this.error = error; this.isLoadingTx = false; diff --git a/frontend/src/app/components/transaction/transaction.component.ts b/frontend/src/app/components/transaction/transaction.component.ts index 673743344..0754f6d18 100644 --- a/frontend/src/app/components/transaction/transaction.component.ts +++ b/frontend/src/app/components/transaction/transaction.component.ts @@ -193,8 +193,10 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { }) ).subscribe((tx) => { if (!tx) { + this.seoService.logSoft404(); return; } + this.seoService.clearSoft404(); this.tx = tx; this.isCached = true; @@ -286,9 +288,11 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { }) ) .subscribe((tx: Transaction) => { - if (!tx) { - return; - } + if (!tx) { + this.seoService.logSoft404(); + return; + } + this.seoService.clearSoft404(); this.tx = tx; this.isCached = false; @@ -340,6 +344,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { }, (error) => { this.error = error; + this.seoService.logSoft404(); this.isLoadingTx = false; } ); @@ -394,6 +399,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { this.waitingForTransaction = true; } this.error = error; + this.seoService.logSoft404(); this.isLoadingTx = false; return of(false); } diff --git a/frontend/src/app/lightning/channel/channel-preview.component.ts b/frontend/src/app/lightning/channel/channel-preview.component.ts index 9f1bea4b8..9c7fdc1d6 100644 --- a/frontend/src/app/lightning/channel/channel-preview.component.ts +++ b/frontend/src/app/lightning/channel/channel-preview.component.ts @@ -54,6 +54,7 @@ export class ChannelPreviewComponent implements OnInit { }), catchError((err) => { this.error = err; + this.seoService.logSoft404(); this.openGraphService.fail('channel-map-' + this.shortId); this.openGraphService.fail('channel-data-' + this.shortId); return of(null); diff --git a/frontend/src/app/lightning/channel/channel.component.ts b/frontend/src/app/lightning/channel/channel.component.ts index d57aa3f01..052225cc3 100644 --- a/frontend/src/app/lightning/channel/channel.component.ts +++ b/frontend/src/app/lightning/channel/channel.component.ts @@ -38,6 +38,7 @@ export class ChannelComponent implements OnInit { }), catchError((err) => { this.error = err; + this.seoService.logSoft404(); return [{ short_id: params.get('short_id') }]; diff --git a/frontend/src/app/lightning/group/group-preview.component.ts b/frontend/src/app/lightning/group/group-preview.component.ts index be23e6178..5fd730931 100644 --- a/frontend/src/app/lightning/group/group-preview.component.ts +++ b/frontend/src/app/lightning/group/group-preview.component.ts @@ -50,6 +50,7 @@ export class GroupPreviewComponent implements OnInit { name: this.slug.replace(/-/gi, ' '), description: '', }; + this.seoService.logSoft404(); this.openGraphService.fail('ln-group-map-' + this.slug); this.openGraphService.fail('ln-group-data-' + this.slug); return of(null); @@ -106,6 +107,7 @@ export class GroupPreviewComponent implements OnInit { }; }), catchError(() => { + this.seoService.logSoft404(); this.openGraphService.fail('ln-group-map-' + this.slug); this.openGraphService.fail('ln-group-data-' + this.slug); return of({ diff --git a/frontend/src/app/lightning/node/node-preview.component.ts b/frontend/src/app/lightning/node/node-preview.component.ts index 4c2782c2d..56753b18b 100644 --- a/frontend/src/app/lightning/node/node-preview.component.ts +++ b/frontend/src/app/lightning/node/node-preview.component.ts @@ -81,6 +81,7 @@ export class NodePreviewComponent implements OnInit { }), catchError(err => { this.error = err; + this.seoService.logSoft404(); this.openGraphService.fail('node-map-' + this.publicKey); this.openGraphService.fail('node-data-' + this.publicKey); return [{ diff --git a/frontend/src/app/lightning/node/node.component.ts b/frontend/src/app/lightning/node/node.component.ts index 47f65007f..6f222688d 100644 --- a/frontend/src/app/lightning/node/node.component.ts +++ b/frontend/src/app/lightning/node/node.component.ts @@ -121,6 +121,7 @@ export class NodeComponent implements OnInit { }), catchError(err => { this.error = err; + this.seoService.logSoft404(); return [{ alias: this.publicKey, public_key: this.publicKey, diff --git a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp-preview.component.ts b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp-preview.component.ts index 759606372..b823a5188 100644 --- a/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp-preview.component.ts +++ b/frontend/src/app/lightning/nodes-per-isp/nodes-per-isp-preview.component.ts @@ -85,6 +85,7 @@ export class NodesPerISPPreview implements OnInit { }), catchError(err => { this.error = err; + this.seoService.logSoft404(); this.openGraphService.fail('isp-map-' + this.id); this.openGraphService.fail('isp-data-' + this.id); return of({ diff --git a/frontend/src/app/services/seo.service.ts b/frontend/src/app/services/seo.service.ts index 5f5d15c89..d536b4938 100644 --- a/frontend/src/app/services/seo.service.ts +++ b/frontend/src/app/services/seo.service.ts @@ -1,5 +1,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 './state.service'; @Injectable({ @@ -13,8 +15,22 @@ export class SeoService { private titleService: Title, private metaService: Meta, private stateService: StateService, + private router: Router, + private activatedRoute: ActivatedRoute, ) { this.stateService.networkChanged$.subscribe((network) => this.network = network); + this.router.events.pipe( + filter(event => event instanceof NavigationEnd), + map(() => this.activatedRoute), + map(route => { + while (route.firstChild) route = route.firstChild; + return route; + }), + filter(route => route.outlet === 'primary'), + switchMap(route => route.data), + ).subscribe((data) => { + this.clearSoft404(); + }); } setTitle(newTitle: string): void { @@ -53,4 +69,14 @@ export class SeoService { ucfirst(str: string) { return str.charAt(0).toUpperCase() + str.slice(1); } + + clearSoft404() { + window['soft404'] = false; + console.log('cleared soft 404'); + } + + logSoft404() { + window['soft404'] = true; + console.log('set soft 404'); + } } diff --git a/unfurler/src/index.ts b/unfurler/src/index.ts index 412dd0f4c..d7589797d 100644 --- a/unfurler/src/index.ts +++ b/unfurler/src/index.ts @@ -179,8 +179,15 @@ class Server { await page.waitForNetworkIdle({ timeout: config.PUPPETEER.RENDER_TIMEOUT || 3000, }); - let html = await page.content(); - return html; + const is404 = await page.evaluate(async () => { + return !!window['soft404']; + }); + if (is404) { + return '404'; + } else { + let html = await page.content(); + return html; + } } catch (e) { if (e instanceof TimeoutError) { let html = await page.content(); @@ -258,7 +265,11 @@ class Server { result = await this.renderUnfurlMeta(rawPath); } if (result && result.length) { - res.send(result); + if (result === '404') { + res.status(404).send(); + } else { + res.send(result); + } } else { res.status(500).send(); }