Warning, /firebird/firebird-ng/src/app/services/url.service.ts is written in an unsupported language. File is not indexed.
0001 /**
0002 * @file url.service.ts
0003 * @description Provides URL resolution services for handling file downloads and conversions in an Angular application.
0004 * The service supports different configurations depending on how the application is served and includes
0005 * protocol aliasing for custom URL schemes.
0006 */
0007
0008 import { Injectable } from '@angular/core';
0009 import { UserConfigService } from "./user-config.service";
0010 import { ServerConfigService } from "./server-config.service";
0011
0012 /**
0013 * @class UrlService
0014 * @description This service resolves URLs for downloading and converting files. It handles different scenarios based
0015 * on whether the application is served as a standalone static site, served by a backend like PyroBird, or
0016 * configured to use a specific API endpoint. It also supports protocol aliases and special URL schemes.
0017 *
0018 * ### Assumptions:
0019 * - The application may be served in various configurations:
0020 * - Standalone static site without backend API.
0021 * - Served by a Flask application (e.g., PyroBird) with a backend API.
0022 * - Served by another service with or without a backend API.
0023 * - The service needs to resolve URLs for files that may:
0024 * - Be accessible over HTTP/HTTPS.
0025 * - Be local files requiring backend API to serve them.
0026 * - Use custom protocols (e.g., `asset://`, `epic://`).
0027 *
0028 * ### Use Cases:
0029 * - **Case 1: Downloading Files**
0030 * - **1.1**: Input URL starts with protocol like `root://`, `http://` or `https://`. The URL is used as is.
0031 * - **1.2**: Input URL has no protocol or is a local file. The service checks if a backend is available and constructs the download endpoint URL.
0032 * - **Case 2: Converting Files**
0033 * - **2.1 & 2.2**: Input URL is converted using the backend 'convert' endpoint, regardless of the original protocol.
0034 *
0035 * ### Protocol Aliases:
0036 * - `asset://`: Points to assets served from the frontend server.
0037 * - `epic://`: Custom protocol replaced with a specific base URL.
0038 *
0039 * ### Examples:
0040 * - Download URL:
0041 * - Input: `https://example.com/file.root`
0042 * - Output: `https://example.com/file.root` (Case 1.1)
0043 * - Download URL:
0044 * - Input: `/path/to/file.root`
0045 * - Output: `<serverAddress>/api/v1/download?f=%2Fpath%2Fto%2Ffile.root` (if backend is available) (Case 1.2)
0046 * - Convert URL:
0047 * - Input: `https://example.com/file.root`, `fileType`, `entries`
0048 * - Output: `<serverAddress>/api/v1/convert/fileType/entries?f=https%3A%2F%2Fexample.com%2Ffile.root`
0049 */
0050
0051 @Injectable({
0052 providedIn: 'root'
0053 })
0054 export class UrlService {
0055
0056 private serverAddress: string = '';
0057 private isBackendAvailable: boolean = false;
0058
0059 // Protocol aliases mapping
0060 private readonly protocolAliases: { [key: string]: string } = {
0061 'epic://': 'https://eic.github.io/epic/artifacts/',
0062 // Add other protocol aliases here if needed
0063 };
0064
0065 constructor(
0066 private userConfigService: UserConfigService,
0067 private serverConfigService: ServerConfigService
0068 ) {
0069 this.initializeConfig();
0070 }
0071
0072 /**
0073 * Initializes the service configuration and subscribes to changes in user and server configurations.
0074 */
0075 private initializeConfig() {
0076 this.updateServerConfig();
0077
0078 // Subscribe to user configuration changes
0079 this.userConfigService.localServerUrl.subject.subscribe(() => {
0080 this.updateServerConfig();
0081 });
0082 this.userConfigService.localServerUseApi.subject.subscribe(() => {
0083 this.updateServerConfig();
0084 });
0085 }
0086
0087 /**
0088 * Updates the backend availability and server address based on current configurations.
0089 */
0090 private updateServerConfig() {
0091 const servedByPyrobird = this.serverConfigService.config.servedByPyrobird;
0092 const userUseApi = this.userConfigService.localServerUseApi.value;
0093 const userServerUrl = this.userConfigService.localServerUrl.value;
0094
0095 this.isBackendAvailable = servedByPyrobird || userUseApi;
0096
0097 if (servedByPyrobird) {
0098 this.serverAddress = this.serverConfigService.config.apiBaseUrl;
0099 } else if (userUseApi) {
0100 this.serverAddress = userServerUrl;
0101 } else {
0102 this.serverAddress = '';
0103 }
0104 }
0105
0106 /**
0107 * Resolves protocol aliases in the URL.
0108 *
0109 * - Replaces 'asset://' with the base URI of the application and the 'assets' path.
0110 * - Replaces any custom protocol aliases defined in the `protocolAliases` map.
0111 *
0112 * @param url The URL to resolve.
0113 * @returns The URL with protocol aliases resolved.
0114 */
0115 private resolveProtocolAliases(url: string): string {
0116 if (url.startsWith('asset://')) {
0117 const assetPath = url.substring('asset://'.length);
0118 // Assets are served from where the frontend is served.
0119 // Ensure there's no double slash
0120 const baseUri = document.baseURI.endsWith('/') ? document.baseURI : `${document.baseURI}/`;
0121 return `${baseUri}assets/${assetPath}`;
0122 }
0123
0124 for (const alias in this.protocolAliases) {
0125 if (url.startsWith(alias)) {
0126 return url.replace(alias, this.protocolAliases[alias]);
0127 }
0128 }
0129 return url;
0130 }
0131
0132 /**
0133 * Checks if a URL is absolute (i.e., starts with a protocol like 'http://').
0134 *
0135 * @param url The URL to check.
0136 * @returns True if the URL is absolute, false otherwise.
0137 */
0138 private isAbsoluteUrl(url: string): boolean {
0139 return /^[a-z][a-z0-9+.-]*:/.test(url);
0140 }
0141
0142 /**
0143 * Resolves the URL for downloading a file.
0144 *
0145 * **Case 1.1**: If the URL is absolute (starts with 'http://' or 'https://'), it is returned as is.
0146 *
0147 * **Case 1.2**: If the URL has no protocol, it uses the backend download endpoint if available.
0148 * Constructs the URL: `<serverAddress>/api/v1/download?f=<encoded inputUrl>`
0149 *
0150 * @param inputUrl The input URL to resolve.
0151 * @returns The resolved URL for downloading.
0152 */
0153 public resolveDownloadUrl(inputUrl: string): string {
0154 inputUrl = this.resolveProtocolAliases(inputUrl);
0155
0156 if (this.isAbsoluteUrl(inputUrl)) {
0157 // Case 1.1: Leave the URL as is
0158 return inputUrl;
0159 } else {
0160 // Case 1.2: Use the download endpoint if available
0161 if (this.isBackendAvailable && this.serverAddress) {
0162 return `${this.serverAddress}/api/v1/download?f=${encodeURIComponent(inputUrl)}`;
0163 } else {
0164 console.warn("Backend is not available to fetch the file");
0165 return inputUrl;
0166 }
0167 }
0168 }
0169
0170 /**
0171 * Resolves the URL for converting a file using the 'convert' endpoint.
0172 *
0173 * **Case 2.1 & 2.2**: Constructs the URL using the backend 'convert' endpoint.
0174 * Constructs the URL: `<serverAddress>/api/v1/convert/<fileType>/<entries>?f=<encoded inputUrl>`
0175 *
0176 * @param inputUrl The input URL to resolve.
0177 * @param fileType The file type for conversion.
0178 * @param entries Additional entries for conversion.
0179 * @returns The resolved URL for conversion.
0180 */
0181 public resolveConvertUrl(inputUrl: string, fileType: string, entries: string): string {
0182 inputUrl = this.resolveProtocolAliases(inputUrl);
0183
0184 if (this.isBackendAvailable && this.serverAddress) {
0185 return `${this.serverAddress}/api/v1/convert/${fileType}/${entries}?f=${encodeURIComponent(inputUrl)}`;
0186 } else {
0187 console.warn("Backend is not available to perform conversion");
0188 return inputUrl;
0189 }
0190 }
0191 }