All files / app/core/services/ipfs ipfs-share-link.service.ts

100% Statements 38/38
94.73% Branches 18/19
100% Functions 12/12
100% Lines 37/37

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148                          1x 66x                   66x             34x 34x     34x 8x       34x 2x 2x     34x             2x 12x               2x 2x             14x   14x 14x       13x   1x                 3x 1x       2x 8x 1x       1x               8x 1x       7x             3x             5x   4x 4x 4x   4x             1x             1x    
import { Injectable, Inject } from '@angular/core';
import { IPFSConfig } from '../../domain/interfaces/ipfs.interface';
import { IPFS_CONFIG } from './ipfs-storage.service';
 
export interface ShareLinkOptions {
  gateway?: string;
  filename?: string;
  download?: boolean;
}
 
@Injectable({
  providedIn: 'root'
})
export class IPFSShareLinkService {
  private readonly publicGateways = [
    'https://ipfs.io',
    'https://gateway.ipfs.io',
    'https://cloudflare-ipfs.com',
    'https://gateway.pinata.cloud',
    'https://ipfs.fleek.co',
    'https://ipfs.infura.io'
  ];
 
  constructor(
    @Inject(IPFS_CONFIG) private config: IPFSConfig
  ) {}
 
  /**
   * Generate a shareable IPFS link for a given CID
   */
  generateShareLink(cid: string, options: ShareLinkOptions = {}): string {
    const gateway = options.gateway || this.getDefaultGateway();
    let link = `${gateway}/ipfs/${cid}`;
    
    // Add filename parameter if provided (helps with browser downloads)
    if (options.filename) {
      link += `?filename=${encodeURIComponent(options.filename)}`;
    }
    
    // Add download parameter if requested
    if (options.download) {
      link += options.filename ? '&' : '?';
      link += 'download=true';
    }
    
    return link;
  }
 
  /**
   * Generate multiple share links for different gateways
   */
  generateMultipleShareLinks(cid: string, options: ShareLinkOptions = {}): string[] {
    return this.publicGateways.map(gateway => 
      this.generateShareLink(cid, { ...options, gateway })
    );
  }
 
  /**
   * Get a shareable link for local IPFS node
   */
  generateLocalShareLink(cid: string, options: ShareLinkOptions = {}): string {
    const localGateway = this.config.gateway || 'http://127.0.0.1:8080';
    return this.generateShareLink(cid, { ...options, gateway: localGateway });
  }
 
  /**
   * Check if a CID is accessible via a gateway
   */
  async checkGatewayAvailability(cid: string, gateway?: string): Promise<boolean> {
    const url = this.generateShareLink(cid, { gateway });
    
    try {
      const response = await fetch(url, { 
        method: 'HEAD',
        signal: AbortSignal.timeout(5000) // 5 second timeout
      });
      return response.ok;
    } catch {
      return false;
    }
  }
 
  /**
   * Find the first available gateway for a CID
   */
  async findAvailableGateway(cid: string): Promise<string | null> {
    // Check local gateway first
    if (await this.checkGatewayAvailability(cid, this.config.gateway)) {
      return this.config.gateway || 'http://127.0.0.1:8080';
    }
 
    // Check public gateways
    for (const gateway of this.publicGateways) {
      if (await this.checkGatewayAvailability(cid, gateway)) {
        return gateway;
      }
    }
 
    return null;
  }
 
  /**
   * Get the default gateway based on configuration
   */
  private getDefaultGateway(): string {
    // If in gateway mode, use configured gateway
    if (this.config.mode === 'gateway' && this.config.gateway) {
      return this.config.gateway;
    }
    
    // Otherwise use a public gateway
    return this.publicGateways[0];
  }
 
  /**
   * Get list of available public gateways
   */
  getPublicGateways(): string[] {
    return [...this.publicGateways];
  }
 
  /**
   * Format bytes to human readable string
   */
  formatBytes(bytes: number): string {
    if (bytes === 0) return '0 Bytes';
    
    const k = 1024;
    const sizes = ['Bytes', 'KB', 'MB', 'GB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
  }
 
  /**
   * Generate IPFS protocol link (for IPFS Companion browser extension)
   */
  generateProtocolLink(cid: string): string {
    return `ipfs://${cid}`;
  }
 
  /**
   * Generate IPNS protocol link
   */
  generateIPNSLink(name: string): string {
    return `ipns://${name}`;
  }
}