UNPKG

dwnpm

Version:

Decentralized Registry Package Manager (DRPM) helps developers publish, install, find and manage Decentralized Packages (DPKs) published to Decentralized Web Nodes (DWNs). DRPM does this by looking up a Decentralized Identifier (DID) to find its DID docum

229 lines (202 loc) 8.02 kB
import { createHash } from 'crypto'; import { Request } from 'express'; import { createReadStream, createWriteStream } from 'fs'; import { ensureDir, exists } from 'fs-extra'; import { access, readFile, writeFile } from 'fs/promises'; import { join } from 'path'; import { pipeline } from 'stream/promises'; import { DRPM_REGISTRY_DIR, DRPM_VERSION_PREFIXES } from '../config.js'; import { Logger } from '../utils/logger.js'; import { PrefixResponse, RegistryGetDpkPath, RegistryResponse, RegistrySaveDpkData, RouteFailureParams, RouteSuccessParams } from '../utils/types.js'; export class RegistryUtils { // Saves the tarball file to path $HOME/.drpm/registry/name/version/name-version.tgz static async saveDpkTarball({ name, version, data }: RegistrySaveDpkData): Promise<boolean | string> { const dpkAtVersionPath = this.getDpkVersionPath({name, version}); try { await this.ensureDpkDir(dpkAtVersionPath); Logger.info(`Ensured dir at ${dpkAtVersionPath}`); } catch (error: any) { Logger.error(`DRegistry: Failed to ensure dir at ${dpkAtVersionPath}`, error); return false; } const dpkTarballPath = this.getDpkTarballPath({name, version}); try { Logger.log(`Saving tarball to ${dpkTarballPath} ...`); await pipeline(data, createWriteStream(dpkTarballPath)); return dpkTarballPath; } catch (error) { Logger.error(`DRegistry: Failed to save dpk tarball to ${dpkTarballPath} `, error); return false; } } // Saves the metadata file to path $HOME/.drpm/registry/name/version/metadata.json static async saveDpkMetadata({ name, version, data }: RegistrySaveDpkData): Promise<boolean> { const dpkAtVersionPath = this.getDpkVersionPath({name, version}); try { await this.ensureDpkDir(dpkAtVersionPath); } catch (error: any) { Logger.error(`DRegistry: Failed to ensure dir at ${dpkAtVersionPath}`, error); return false; } const dpkMetadataPath = this.getDpkMetadataPath({name, version}); try { await writeFile(dpkMetadataPath, JSON.stringify(data, null, 2)); Logger.info(`Saved metadata to ${dpkMetadataPath}`); return true; } catch (error: any) { Logger.error(`DRegistry: Failed to save dpk metadata to ${dpkMetadataPath} `, error); return false; } } // Ensure the path to the $HOME/.drpm/registry/{dpk, dpk/version} directory exists static async ensureDpkDir(path: string): Promise<any> { try { await ensureDir(path); Logger.info(`Ensured dir at ${path}`); return true; } catch (error: any) { Logger.error(`DRegistry: Failed to ensure dir at ${path}`, error); throw error; } } // Get the path to the $HOME/.drpm/registry/name directory static getDpkPath({name}: {name: string}): string { Logger.info(`Getting dpk dir path for ${name}`); return join(DRPM_REGISTRY_DIR, name); }; // Get the path to the $HOME/.drpm/registry/name directory static getDpkLatestPath({name}: {name: string}): string { Logger.info(`Getting dpk dir path for ${name}`); return join(DRPM_REGISTRY_DIR, name, 'latest'); }; // Get the path to the $HOME/.drpm/registry/name/version directory static getDpkVersionPath({name, version}: RegistryGetDpkPath): string { Logger.info(`Getting dpkVersion dir path for ${name}@${version}`); return join(DRPM_REGISTRY_DIR, name, version); }; // Get the path to the $HOME/.drpm/registry/name/version/metadata.json file // e.g. /Users/username/.drpm/registry/tool5/6.1.0/metadata.json static getDpkMetadataPath({name, version}: RegistryGetDpkPath): string { Logger.info(`Getting metadata path for ${name}@${version}`); return join(DRPM_REGISTRY_DIR, name, version, 'metadata.json'); }; // Get the path to the $HOME/.drpm/registry/dpk/version/dpk-version.tgz file // e.g. /Users/username/.drpm/registry/tool5/6.1.0/tool5-6.1.0.tgz static getDpkTarballPath({name, version}: RegistryGetDpkPath): string { Logger.info(`Getting tarball path for ${name}@${version}`); return join(DRPM_REGISTRY_DIR, name, version, `${name}-${version}.tgz`); }; static async saveMetadataToPath({path, metadata}: {path: string; metadata: any}): Promise<boolean> { try { await writeFile(path, JSON.stringify(metadata, null, 2)); Logger.info(`Saved metadata to ${path}`); return true; } catch (error: any) { Logger.error(`DRegistry: Failed to save metadata to ${path} `, error); return false; } } static async saveTarballToPath({path, tarball}: {path: string; tarball: any}): Promise<boolean> { try { await pipeline(tarball, createWriteStream(path)); Logger.info(`Saved tarball to ${path}`); return true; } catch (error: any) { Logger.error(`DRegistry: Failed to save tarball to ${path} `, error); return false; } } static async loadDpkMetadata(path: string): Promise<any> { try { if(!await exists(path)) { Logger.info(`DRegistry: metadata.json does not exist at path ${path}`); return null; } await access(path); const metadata = JSON.parse(await readFile(path, 'utf8')); return metadata; } catch (error: any) { Logger.error(`DRegistry: metadata.json does not exist at path ${path}`, error); throw error; } }; static async loadDpkTarball(path: string): Promise<any> { try { if(!await exists(path)) { Logger.info(`DRegistry: tarball does not exist at path ${path}`); return null; } await access(path); return path; } catch (error: any) { Logger.error(`DRegistry: tarball does not exist at path ${path}`, error); throw error; } }; static createMetadata(metadata: any): any { const {name, version} = metadata; metadata.dist = this.createDist(name, version); return { name, 'dist-tags' : { latest: version }, versions : { [version]: { ...metadata }}, }; } static createDist(name: string, version: string): any { return { shasum : '', tarball : `http://registry.drpm.software/@drpm/${name}/-/${name}-${version}.tgz` }; } static updateMetadata(existingMetadata: any, newMetadata: any): any { const {name, version} = newMetadata; newMetadata.dist = !newMetadata.dist.tarball ? this.createDist(name, version) : newMetadata.dist; const tgzPath = join(DRPM_REGISTRY_DIR, name, `${name}-${version}.tgz`); newMetadata.dist.shasum = this.calculateShasum1(tgzPath); existingMetadata['dist-tags'].latest = version; existingMetadata.versions[version] = newMetadata; return existingMetadata; } static calculateShasum1(path: string): any { const hash = createHash('sha1'); const stream = createReadStream(path); return new Promise((resolve, reject) => { stream.on('data', chunk => hash.update(chunk)); stream.on('end', () => { const shasum = hash.digest('hex'); resolve(shasum); }); stream.on('error', (error) => reject(error)); }); } static isPrefixed(semver: string): boolean { return DRPM_VERSION_PREFIXES.some((prefix: string) => semver.startsWith(prefix)); } static findPrefix(prefixed: string): PrefixResponse { const prefix = DRPM_VERSION_PREFIXES.find((prefix: string) => prefixed.startsWith(prefix)); return !prefix ? { version: prefixed, prefix: '' } : { version: prefixed.slice(prefix.length), prefix }; } static checkReqParams(params: Request['params']): string[][] { return Object.entries(params).filter(([k, v]) => !v && k); } static routeFailure({code, status, error}: RouteFailureParams): RegistryResponse { return { ok: false, code: code ?? 404, status: status ?? 'Not Found', error }; } static routeSuccess({code, status, data}: RouteSuccessParams): RegistryResponse { return { ok: true, code: code ?? 200, status: status ?? 'OK', data }; } }